mkdocs-terok 0.5.6__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.
- mkdocs_terok-0.5.6/LICENSE +5 -0
- mkdocs_terok-0.5.6/LICENSES/0BSD.txt +5 -0
- mkdocs_terok-0.5.6/PKG-INFO +73 -0
- mkdocs_terok-0.5.6/README.md +35 -0
- mkdocs_terok-0.5.6/pyproject.toml +134 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/__init__.py +26 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/_assets/extra.css +211 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/_assets/mermaid_zoom.js +128 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/ci_map.py +174 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/config_reference.py +348 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/module_map.py +561 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/plugin.py +241 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/quality_report.py +708 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/ref_pages.py +80 -0
- mkdocs_terok-0.5.6/src/mkdocs_terok/test_map.py +258 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Copyright © 2026 by Jiří Vyskočil <jiri@vyskocil.com>
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
|
4
|
+
|
|
5
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Copyright © 2026 by Jiří Vyskočil <jiri@vyskocil.com>
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
|
4
|
+
|
|
5
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mkdocs-terok
|
|
3
|
+
Version: 0.5.6
|
|
4
|
+
Summary: Shared ProperDocs documentation generators for terok projects
|
|
5
|
+
License: 0BSD
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
License-File: LICENSES/0BSD.txt
|
|
8
|
+
Keywords: properdocs,mkdocs,documentation,quality-report,ci-map
|
|
9
|
+
Author: Jiri Vyskocil
|
|
10
|
+
Author-email: jiri@vyskocil.com
|
|
11
|
+
Maintainer: Jiri Vyskocil
|
|
12
|
+
Maintainer-email: jiri@vyskocil.com
|
|
13
|
+
Requires-Python: >=3.12,<4.0
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Framework :: MkDocs
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: License :: OSI Approved
|
|
19
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
20
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
21
|
+
Classifier: Programming Language :: Python
|
|
22
|
+
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
|
+
Classifier: Topic :: Documentation
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Dist: PyYAML (>=6.0)
|
|
29
|
+
Requires-Dist: properdocs (>=1.6.7)
|
|
30
|
+
Project-URL: Changelog, https://github.com/terok-ai/mkdocs-terok/releases
|
|
31
|
+
Project-URL: Documentation, https://terok-ai.github.io/mkdocs-terok/
|
|
32
|
+
Project-URL: Homepage, https://terok.ai/
|
|
33
|
+
Project-URL: Issues, https://github.com/terok-ai/mkdocs-terok/issues
|
|
34
|
+
Project-URL: Repository, https://github.com/terok-ai/mkdocs-terok
|
|
35
|
+
Project-URL: Security, https://github.com/terok-ai/mkdocs-terok/security/policy
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# mkdocs-terok
|
|
39
|
+
|
|
40
|
+
[](https://opensource.org/license/0bsd)
|
|
41
|
+
[](https://api.reuse.software/info/github.com/terok-ai/mkdocs-terok)
|
|
42
|
+
[](https://codecov.io/gh/terok-ai/mkdocs-terok)
|
|
43
|
+
|
|
44
|
+
Shared [ProperDocs](https://properdocs.org/) documentation generators for terok projects.
|
|
45
|
+
|
|
46
|
+
Provides reusable modules for generating CI workflow maps, integration test maps,
|
|
47
|
+
code quality reports, API reference pages, and config reference documentation from
|
|
48
|
+
Pydantic models. A built-in `terok` ProperDocs plugin drives all generators
|
|
49
|
+
automatically; the generator modules themselves never import the doc engine and can
|
|
50
|
+
also be used standalone via `mkdocs-gen-files` shims.
|
|
51
|
+
|
|
52
|
+
The quality report module can optionally parse output from
|
|
53
|
+
[scc](https://github.com/boyter/scc),
|
|
54
|
+
[complexipy](https://github.com/rohaquinern/complexipy),
|
|
55
|
+
[tach](https://github.com/gauge-sh/tach),
|
|
56
|
+
[vulture](https://github.com/jendrikseipp/vulture), and
|
|
57
|
+
[docstr-coverage](https://github.com/HunterMcGushion/docstr_coverage).
|
|
58
|
+
When any of these tools is absent, the corresponding report section degrades
|
|
59
|
+
gracefully to a warning admonition.
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
Add to your project's `pyproject.toml` as a docs-build dependency:
|
|
64
|
+
|
|
65
|
+
```toml
|
|
66
|
+
[tool.poetry.group.docs.dependencies]
|
|
67
|
+
mkdocs-terok = "^0.5"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
[0BSD](https://opensource.org/license/0bsd) — use freely, no strings attached.
|
|
73
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# mkdocs-terok
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/license/0bsd)
|
|
4
|
+
[](https://api.reuse.software/info/github.com/terok-ai/mkdocs-terok)
|
|
5
|
+
[](https://codecov.io/gh/terok-ai/mkdocs-terok)
|
|
6
|
+
|
|
7
|
+
Shared [ProperDocs](https://properdocs.org/) documentation generators for terok projects.
|
|
8
|
+
|
|
9
|
+
Provides reusable modules for generating CI workflow maps, integration test maps,
|
|
10
|
+
code quality reports, API reference pages, and config reference documentation from
|
|
11
|
+
Pydantic models. A built-in `terok` ProperDocs plugin drives all generators
|
|
12
|
+
automatically; the generator modules themselves never import the doc engine and can
|
|
13
|
+
also be used standalone via `mkdocs-gen-files` shims.
|
|
14
|
+
|
|
15
|
+
The quality report module can optionally parse output from
|
|
16
|
+
[scc](https://github.com/boyter/scc),
|
|
17
|
+
[complexipy](https://github.com/rohaquinern/complexipy),
|
|
18
|
+
[tach](https://github.com/gauge-sh/tach),
|
|
19
|
+
[vulture](https://github.com/jendrikseipp/vulture), and
|
|
20
|
+
[docstr-coverage](https://github.com/HunterMcGushion/docstr_coverage).
|
|
21
|
+
When any of these tools is absent, the corresponding report section degrades
|
|
22
|
+
gracefully to a warning admonition.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Add to your project's `pyproject.toml` as a docs-build dependency:
|
|
27
|
+
|
|
28
|
+
```toml
|
|
29
|
+
[tool.poetry.group.docs.dependencies]
|
|
30
|
+
mkdocs-terok = "^0.5"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## License
|
|
34
|
+
|
|
35
|
+
[0BSD](https://opensource.org/license/0bsd) — use freely, no strings attached.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry-core>=1.9.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
|
3
|
+
build-backend = "poetry_dynamic_versioning.backend"
|
|
4
|
+
|
|
5
|
+
[tool.poetry]
|
|
6
|
+
name = "mkdocs-terok"
|
|
7
|
+
version = "0.5.6"
|
|
8
|
+
description = "Shared ProperDocs documentation generators for terok projects"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "0BSD"
|
|
11
|
+
authors = ["Jiri Vyskocil <jiri@vyskocil.com>"]
|
|
12
|
+
keywords = ["properdocs", "mkdocs", "documentation", "quality-report", "ci-map"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Framework :: MkDocs",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: BSD License",
|
|
19
|
+
"Operating System :: POSIX :: Linux",
|
|
20
|
+
"Programming Language :: Python",
|
|
21
|
+
"Topic :: Documentation",
|
|
22
|
+
"Typing :: Typed",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
packages = [
|
|
26
|
+
{ include = "mkdocs_terok", from = "src" },
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
include = [
|
|
30
|
+
{ path = "src/mkdocs_terok/_assets/*.css" },
|
|
31
|
+
{ path = "src/mkdocs_terok/_assets/*.js" },
|
|
32
|
+
]
|
|
33
|
+
maintainers = ["Jiri Vyskocil <jiri@vyskocil.com>"]
|
|
34
|
+
homepage = "https://terok.ai/"
|
|
35
|
+
repository = "https://github.com/terok-ai/mkdocs-terok"
|
|
36
|
+
documentation = "https://terok-ai.github.io/mkdocs-terok/"
|
|
37
|
+
|
|
38
|
+
[tool.poetry.dependencies]
|
|
39
|
+
python = ">=3.12,<4.0"
|
|
40
|
+
PyYAML = ">=6.0"
|
|
41
|
+
properdocs = ">=1.6.7"
|
|
42
|
+
|
|
43
|
+
[tool.poetry.plugins."mkdocs.plugins"]
|
|
44
|
+
terok = "mkdocs_terok.plugin:TerokPlugin"
|
|
45
|
+
|
|
46
|
+
[tool.poetry.group.dev.dependencies]
|
|
47
|
+
ruff = ">=0.8"
|
|
48
|
+
docstr-coverage = "^2.3.2"
|
|
49
|
+
vulture = ">=2.12"
|
|
50
|
+
reuse = "^6.2.0"
|
|
51
|
+
|
|
52
|
+
[tool.poetry.group.test.dependencies]
|
|
53
|
+
pytest = ">=7.0"
|
|
54
|
+
pytest-cov = ">=4.0"
|
|
55
|
+
pydantic = ">=2.6"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
[tool.poetry.urls]
|
|
59
|
+
Issues = "https://github.com/terok-ai/mkdocs-terok/issues"
|
|
60
|
+
Changelog = "https://github.com/terok-ai/mkdocs-terok/releases"
|
|
61
|
+
Security = "https://github.com/terok-ai/mkdocs-terok/security/policy"
|
|
62
|
+
|
|
63
|
+
[tool.pytest.ini_options]
|
|
64
|
+
testpaths = ["tests"]
|
|
65
|
+
python_files = ["test_*.py"]
|
|
66
|
+
python_classes = ["Test*", "*Tests"]
|
|
67
|
+
python_functions = ["test_*"]
|
|
68
|
+
addopts = "-v --tb=short"
|
|
69
|
+
|
|
70
|
+
[tool.poetry.group.docs]
|
|
71
|
+
optional = true
|
|
72
|
+
|
|
73
|
+
[tool.poetry.group.docs.dependencies]
|
|
74
|
+
mkdocs-material = ">=9.7.6"
|
|
75
|
+
mkdocs-gen-files = ">=0.5"
|
|
76
|
+
mkdocs-literate-nav = ">=0.6"
|
|
77
|
+
mkdocstrings = {version = ">=0.26", extras = ["python"]}
|
|
78
|
+
pydantic = ">=2.6"
|
|
79
|
+
complexipy = ">=5.0"
|
|
80
|
+
tach = ">=0.34"
|
|
81
|
+
|
|
82
|
+
[tool.ruff]
|
|
83
|
+
target-version = "py312"
|
|
84
|
+
line-length = 100
|
|
85
|
+
src = ["src", "tests", "docs"]
|
|
86
|
+
|
|
87
|
+
[tool.ruff.lint]
|
|
88
|
+
select = [
|
|
89
|
+
"E", # pycodestyle errors
|
|
90
|
+
"W", # pycodestyle warnings
|
|
91
|
+
"F", # pyflakes
|
|
92
|
+
"I", # isort
|
|
93
|
+
"B", # flake8-bugbear
|
|
94
|
+
"C4", # flake8-comprehensions
|
|
95
|
+
"UP", # pyupgrade
|
|
96
|
+
"SIM", # flake8-simplify
|
|
97
|
+
]
|
|
98
|
+
ignore = [
|
|
99
|
+
"E501", # line too long (handled by formatter)
|
|
100
|
+
"B904", # raise from in except
|
|
101
|
+
"SIM102", # nested if statements
|
|
102
|
+
"SIM105", # contextlib.suppress vs try/except/pass
|
|
103
|
+
"SIM108", # use ternary operator
|
|
104
|
+
"SIM117", # nested with statements
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
[tool.ruff.lint.per-file-ignores]
|
|
108
|
+
"tests/**" = ["E402"]
|
|
109
|
+
"docs/**" = ["E402"]
|
|
110
|
+
|
|
111
|
+
[tool.ruff.lint.isort]
|
|
112
|
+
known-first-party = ["mkdocs_terok"]
|
|
113
|
+
combine-as-imports = true
|
|
114
|
+
|
|
115
|
+
[tool.ruff.format]
|
|
116
|
+
quote-style = "double"
|
|
117
|
+
indent-style = "space"
|
|
118
|
+
docstring-code-format = true
|
|
119
|
+
|
|
120
|
+
[tool.coverage.run]
|
|
121
|
+
source = ["mkdocs_terok"]
|
|
122
|
+
relative_files = true
|
|
123
|
+
|
|
124
|
+
[tool.coverage.xml]
|
|
125
|
+
output = "reports/coverage.xml"
|
|
126
|
+
|
|
127
|
+
[tool.poetry-dynamic-versioning]
|
|
128
|
+
enable = false
|
|
129
|
+
vcs = "git"
|
|
130
|
+
pattern = "^v(?P<base>\\d+\\.\\d+\\.\\d+)$"
|
|
131
|
+
style = "pep440"
|
|
132
|
+
|
|
133
|
+
[tool.poetry-dynamic-versioning.substitution]
|
|
134
|
+
folders = [{path = "src"}]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Jiri Vyskocil
|
|
2
|
+
# SPDX-License-Identifier: 0BSD
|
|
3
|
+
|
|
4
|
+
"""Shared ProperDocs documentation generators for terok projects.
|
|
5
|
+
|
|
6
|
+
Provides reusable modules for CI maps, test maps, quality reports,
|
|
7
|
+
API reference pages, and Pydantic config reference rendering.
|
|
8
|
+
The ``terok`` ProperDocs plugin wraps all generators; individual modules
|
|
9
|
+
remain usable standalone (they never import properdocs themselves).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
__version__ = "0.5.6" # managed by poetry-dynamic-versioning
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def brand_css_path() -> Path:
|
|
20
|
+
"""Return the filesystem path to the shared brand CSS file."""
|
|
21
|
+
return Path(__file__).parent / "_assets" / "extra.css"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def mermaid_zoom_js_path() -> Path:
|
|
25
|
+
"""Return the filesystem path to the Mermaid diagram zoom script."""
|
|
26
|
+
return Path(__file__).parent / "_assets" / "mermaid_zoom.js"
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/* Black/Red/Gold palette with subdued neutrals. */
|
|
2
|
+
:root {
|
|
3
|
+
--terok-black: #0b0b0b;
|
|
4
|
+
--terok-red: #a31219;
|
|
5
|
+
--terok-red-dark: #7f0f14;
|
|
6
|
+
--terok-red-pure: #c00000;
|
|
7
|
+
--terok-red-bright: #e04b3f;
|
|
8
|
+
--terok-gold: #c79a22;
|
|
9
|
+
--terok-gold-bright: #d8b454;
|
|
10
|
+
--terok-gold-link: #7a6200;
|
|
11
|
+
--terok-red-hover: #8b0000;
|
|
12
|
+
|
|
13
|
+
--md-text-font: "Ubuntu", sans-serif;
|
|
14
|
+
--md-code-font: "Ubuntu Mono", monospace;
|
|
15
|
+
|
|
16
|
+
--md-primary-fg-color: var(--terok-red-dark);
|
|
17
|
+
--md-primary-fg-color--light: #8f1117;
|
|
18
|
+
--md-primary-fg-color--dark: #650b10;
|
|
19
|
+
|
|
20
|
+
--md-primary-bg-color: #f7f2e8;
|
|
21
|
+
--md-primary-bg-color--light: #f7f2e8;
|
|
22
|
+
--md-primary-bg-color--dark: #e7decc;
|
|
23
|
+
|
|
24
|
+
--md-accent-fg-color: var(--terok-gold);
|
|
25
|
+
--md-accent-fg-color--transparent: rgba(199, 154, 34, 0.12);
|
|
26
|
+
|
|
27
|
+
--md-typeset-a-color: var(--terok-gold-link);
|
|
28
|
+
--md-typeset-mark-color: var(--terok-gold-bright);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
body,
|
|
32
|
+
.md-typeset,
|
|
33
|
+
.md-header,
|
|
34
|
+
.md-tabs,
|
|
35
|
+
.md-nav,
|
|
36
|
+
.md-search__input {
|
|
37
|
+
font-family: var(--md-text-font);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
pre,
|
|
41
|
+
code,
|
|
42
|
+
kbd,
|
|
43
|
+
samp {
|
|
44
|
+
font-family: var(--md-code-font);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.md-header,
|
|
48
|
+
.md-tabs,
|
|
49
|
+
.md-header__inner,
|
|
50
|
+
.md-tabs__inner {
|
|
51
|
+
background-color: var(--terok-red-dark) !important;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.md-typeset a,
|
|
55
|
+
.md-typeset a:visited {
|
|
56
|
+
color: var(--terok-gold-link);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.md-typeset a:hover,
|
|
60
|
+
.md-nav__link:hover,
|
|
61
|
+
.md-tabs__link:hover,
|
|
62
|
+
.md-header__button:hover {
|
|
63
|
+
color: var(--terok-red-hover) !important;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.md-nav__item--active > .md-nav__link,
|
|
67
|
+
.md-nav__link--active,
|
|
68
|
+
.md-nav--secondary .md-nav__link--active {
|
|
69
|
+
color: var(--terok-gold-bright) !important;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.md-nav--secondary .md-nav__link--active::before,
|
|
73
|
+
.md-nav--secondary .md-nav__link--active::after {
|
|
74
|
+
background-color: var(--terok-gold-bright);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.md-nav--secondary .md-nav__link--active {
|
|
78
|
+
border-left-color: var(--terok-gold-bright);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.md-tabs__link--active {
|
|
82
|
+
color: inherit !important;
|
|
83
|
+
font-weight: 700;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#codecov-treemap-img {
|
|
87
|
+
display: block;
|
|
88
|
+
min-width: 300px;
|
|
89
|
+
width: 60%;
|
|
90
|
+
margin: 0 auto;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
[data-md-color-scheme="default"] {
|
|
94
|
+
--md-primary-fg-color: var(--terok-red-dark);
|
|
95
|
+
--md-primary-fg-color--light: #8f1117;
|
|
96
|
+
--md-primary-fg-color--dark: #650b10;
|
|
97
|
+
--md-default-bg-color: #f7f2e8;
|
|
98
|
+
--md-default-fg-color: #111111;
|
|
99
|
+
--md-default-fg-color--light: #2b2b2b;
|
|
100
|
+
--md-default-fg-color--lighter: #4a4a4a;
|
|
101
|
+
--md-default-fg-color--lightest: #6a6a6a;
|
|
102
|
+
--md-code-bg-color: #efe6d6;
|
|
103
|
+
--md-code-fg-color: #111111;
|
|
104
|
+
--md-footer-bg-color: #0b0b0b;
|
|
105
|
+
--md-footer-fg-color: #f7f2e8;
|
|
106
|
+
--md-footer-fg-color--light: #d9cfbd;
|
|
107
|
+
--md-footer-fg-color--lighter: #bdb39f;
|
|
108
|
+
--md-footer-fg-color--lightest: #a79c87;
|
|
109
|
+
--md-accent-fg-color: var(--terok-gold);
|
|
110
|
+
--md-accent-fg-color--transparent: rgba(199, 154, 34, 0.12);
|
|
111
|
+
--md-typeset-a-color: var(--terok-gold-link);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
[data-md-color-scheme="slate"] {
|
|
115
|
+
--md-primary-fg-color: var(--terok-red-dark);
|
|
116
|
+
--md-primary-fg-color--light: #8f1117;
|
|
117
|
+
--md-primary-fg-color--dark: #650b10;
|
|
118
|
+
--md-default-bg-color: #0b0b0b;
|
|
119
|
+
--md-default-fg-color: #f2ead9;
|
|
120
|
+
--md-default-fg-color--light: #d8cfbf;
|
|
121
|
+
--md-default-fg-color--lighter: #bcb4a5;
|
|
122
|
+
--md-default-fg-color--lightest: #a19a8d;
|
|
123
|
+
--md-code-bg-color: #151515;
|
|
124
|
+
--md-code-fg-color: #f2ead9;
|
|
125
|
+
--md-footer-bg-color: #000000;
|
|
126
|
+
--md-footer-fg-color: #f2ead9;
|
|
127
|
+
--md-footer-fg-color--light: #d8cfbf;
|
|
128
|
+
--md-footer-fg-color--lighter: #bcb4a5;
|
|
129
|
+
--md-footer-fg-color--lightest: #a19a8d;
|
|
130
|
+
|
|
131
|
+
--md-primary-bg-color: #f2ead9;
|
|
132
|
+
--md-primary-bg-color--light: #f2ead9;
|
|
133
|
+
--md-primary-bg-color--dark: #d8cfbf;
|
|
134
|
+
|
|
135
|
+
--md-accent-fg-color: var(--terok-gold-bright);
|
|
136
|
+
--md-accent-fg-color--transparent: rgba(216, 180, 84, 0.14);
|
|
137
|
+
--md-typeset-a-color: var(--terok-gold-bright);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ── Mermaid diagram zoom overlay ───────────────────────────── */
|
|
141
|
+
.mermaid-zoom-wrapper {
|
|
142
|
+
position: relative;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.mermaid-zoom-btn {
|
|
146
|
+
position: absolute;
|
|
147
|
+
top: 4px;
|
|
148
|
+
right: 4px;
|
|
149
|
+
z-index: 1;
|
|
150
|
+
padding: 2px 8px;
|
|
151
|
+
border: 1px solid var(--md-default-fg-color--lightest);
|
|
152
|
+
border-radius: 4px;
|
|
153
|
+
background: var(--md-default-bg-color);
|
|
154
|
+
color: var(--md-default-fg-color--light);
|
|
155
|
+
font: 0.7rem var(--md-text-font);
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.mermaid-zoom-overlay {
|
|
160
|
+
display: none;
|
|
161
|
+
position: fixed;
|
|
162
|
+
inset: 0;
|
|
163
|
+
z-index: 9999;
|
|
164
|
+
background: rgba(0, 0, 0, 0.75);
|
|
165
|
+
backdrop-filter: blur(4px);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.mermaid-zoom-overlay.mermaid-zoom-active {
|
|
169
|
+
display: flex;
|
|
170
|
+
align-items: center;
|
|
171
|
+
justify-content: center;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.mermaid-zoom-viewport {
|
|
175
|
+
display: flex;
|
|
176
|
+
align-items: center;
|
|
177
|
+
justify-content: center;
|
|
178
|
+
width: calc(100vw - 4rem);
|
|
179
|
+
height: calc(100vh - 4rem);
|
|
180
|
+
padding: 1rem;
|
|
181
|
+
border-radius: 8px;
|
|
182
|
+
background: var(--md-default-bg-color);
|
|
183
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|
184
|
+
overflow: auto;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.mermaid-zoom-viewport svg {
|
|
188
|
+
max-width: 100%;
|
|
189
|
+
max-height: 100%;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.mermaid-zoom-close {
|
|
193
|
+
position: absolute;
|
|
194
|
+
top: 0.75rem;
|
|
195
|
+
right: 0.75rem;
|
|
196
|
+
z-index: 10000;
|
|
197
|
+
width: 2rem;
|
|
198
|
+
height: 2rem;
|
|
199
|
+
border: none;
|
|
200
|
+
border-radius: 50%;
|
|
201
|
+
background: var(--terok-red-dark);
|
|
202
|
+
color: #fff;
|
|
203
|
+
font-size: 1.1rem;
|
|
204
|
+
line-height: 1;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
transition: background 0.15s;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.mermaid-zoom-close:hover {
|
|
210
|
+
background: var(--terok-red);
|
|
211
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Jiri Vyskocil
|
|
2
|
+
// SPDX-License-Identifier: 0BSD
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Adds an "Enlarge" button to each rendered Mermaid diagram.
|
|
6
|
+
* Clicking it opens the diagram in a fullscreen overlay (lightbox-style).
|
|
7
|
+
* Close via backdrop click, the X button, or Escape.
|
|
8
|
+
*/
|
|
9
|
+
;(() => {
|
|
10
|
+
const BUTTON_LABEL = "\u2922 Enlarge"
|
|
11
|
+
|
|
12
|
+
// ── Main action ────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/** Scan the DOM for rendered mermaid SVGs and attach buttons. */
|
|
15
|
+
function scan() {
|
|
16
|
+
document.querySelectorAll("pre.mermaid, .mermaid").forEach((el) => {
|
|
17
|
+
if (el.querySelector("svg") || el.tagName === "SVG") attachButton(el)
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Wrap a rendered mermaid container and inject the enlarge button. */
|
|
22
|
+
function attachButton(container) {
|
|
23
|
+
if (container.dataset.zoomAttached) return
|
|
24
|
+
container.dataset.zoomAttached = "1"
|
|
25
|
+
|
|
26
|
+
const wrapper = document.createElement("div")
|
|
27
|
+
wrapper.className = "mermaid-zoom-wrapper"
|
|
28
|
+
container.parentNode.insertBefore(wrapper, container)
|
|
29
|
+
wrapper.appendChild(container)
|
|
30
|
+
|
|
31
|
+
const btn = document.createElement("button")
|
|
32
|
+
btn.className = "mermaid-zoom-btn"
|
|
33
|
+
btn.textContent = BUTTON_LABEL
|
|
34
|
+
btn.addEventListener("click", () => {
|
|
35
|
+
openOverlay(container.querySelector("svg") ?? container)
|
|
36
|
+
})
|
|
37
|
+
container.before(btn)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Overlay mechanics ──────────────────────────────────
|
|
41
|
+
|
|
42
|
+
function openOverlay(svgSource) {
|
|
43
|
+
const overlay =
|
|
44
|
+
document.querySelector(".mermaid-zoom-overlay") || createOverlay()
|
|
45
|
+
const viewport = overlay.querySelector(".mermaid-zoom-viewport")
|
|
46
|
+
viewport.innerHTML = ""
|
|
47
|
+
const clone = svgSource.cloneNode(true)
|
|
48
|
+
clone.removeAttribute("height")
|
|
49
|
+
clone.removeAttribute("width")
|
|
50
|
+
clone.style.maxWidth = "100%"
|
|
51
|
+
clone.style.maxHeight = "100%"
|
|
52
|
+
clone.style.width = "auto"
|
|
53
|
+
clone.style.height = "auto"
|
|
54
|
+
viewport.appendChild(clone)
|
|
55
|
+
overlay.classList.add("mermaid-zoom-active")
|
|
56
|
+
document.addEventListener("keydown", onEscape)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function closeOverlay(overlay) {
|
|
60
|
+
overlay.classList.remove("mermaid-zoom-active")
|
|
61
|
+
document.removeEventListener("keydown", onEscape)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function onEscape(e) {
|
|
65
|
+
if (e.key === "Escape") {
|
|
66
|
+
const overlay = document.querySelector(".mermaid-zoom-overlay")
|
|
67
|
+
if (overlay) closeOverlay(overlay)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Build the overlay element (singleton, appended on first use). */
|
|
72
|
+
function createOverlay() {
|
|
73
|
+
const overlay = document.createElement("div")
|
|
74
|
+
overlay.className = "mermaid-zoom-overlay"
|
|
75
|
+
overlay.addEventListener("click", (e) => {
|
|
76
|
+
if (e.target === overlay) closeOverlay(overlay)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const close = document.createElement("button")
|
|
80
|
+
close.className = "mermaid-zoom-close"
|
|
81
|
+
close.textContent = "\u2715"
|
|
82
|
+
close.title = "Close"
|
|
83
|
+
close.addEventListener("click", () => closeOverlay(overlay))
|
|
84
|
+
|
|
85
|
+
const viewport = document.createElement("div")
|
|
86
|
+
viewport.className = "mermaid-zoom-viewport"
|
|
87
|
+
|
|
88
|
+
overlay.append(close, viewport)
|
|
89
|
+
document.body.appendChild(overlay)
|
|
90
|
+
return overlay
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── Initialization ─────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
// Initial scan once DOM + mermaid rendering settle.
|
|
96
|
+
if (document.readyState === "loading") {
|
|
97
|
+
document.addEventListener("DOMContentLoaded", () => setTimeout(scan, 2000))
|
|
98
|
+
} else {
|
|
99
|
+
setTimeout(scan, 2000)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Catch diagrams rendered after initial load (e.g. instant navigation).
|
|
103
|
+
const observer = new MutationObserver((mutations) => {
|
|
104
|
+
for (const m of mutations) {
|
|
105
|
+
for (const node of m.addedNodes) {
|
|
106
|
+
if (node.nodeType !== 1) continue
|
|
107
|
+
if (
|
|
108
|
+
(node.matches?.(".mermaid, pre.mermaid") && node.querySelector("svg")) ||
|
|
109
|
+
node.querySelector?.(".mermaid svg, pre.mermaid svg") ||
|
|
110
|
+
(node.tagName === "svg" && node.parentElement?.matches?.(".mermaid, pre.mermaid"))
|
|
111
|
+
) {
|
|
112
|
+
setTimeout(scan, 200)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
function startObserver() {
|
|
120
|
+
observer.observe(document.body, { childList: true, subtree: true })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (document.body) {
|
|
124
|
+
startObserver()
|
|
125
|
+
} else {
|
|
126
|
+
document.addEventListener("DOMContentLoaded", startObserver, { once: true })
|
|
127
|
+
}
|
|
128
|
+
})()
|