Skip to content

Commit e5ed630

Browse files
committed
Stable | New features and SEO
1 parent 6ec1903 commit e5ed630

16 files changed

Lines changed: 717 additions & 220 deletions

doc/sphinx/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ else
2929
OPEN_CMD := start
3030
endif
3131

32-
.PHONY: help clean doc-deps doc-html doc-clean-dirs doc-doxygen doc-all $(SPHINX_TARGETS) doc-latexpdf doc-latexpdf
32+
.PHONY: help clean doc-deps doc-html doc-clean-dirs doc-doxygen doc-all $(SPHINX_TARGETS) doc-latexpdf doc-custom
3333

3434
# Default target
3535
help:
@@ -120,6 +120,7 @@ doc-all: doc-clean-dirs doc-doxygen doc-deps doc-html doc-latexpdf
120120
@echo " PDF: $(PDFDIR)/MOLE-docs.pdf"
121121
@echo " API: ../doxygen/cpp/"
122122
@echo "================================================================"
123+
123124
# Explicit Sphinx targets
124125
$(SPHINX_TARGETS):
125126
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS)

doc/sphinx/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ Before building the documentation, ensure you have:
112112
python3 -m pip install -r requirements.txt
113113
```
114114

115+
Some additional packages have been added to enhance documentation features:
116+
- `sphinx-togglebutton`: For collapsible content
117+
- `sphinx-fontawesome`: For FontAwesome icons
118+
- `sphinxcontrib-mermaid`: For Mermaid.js diagrams
119+
- `sphinxext-altair`: For Altair chart integration
120+
- `Pillow`: For image processing including dark mode logo
121+
115122
### Building Steps
116123

117124
All commands should be run from the `doc/sphinx` directory:

doc/sphinx/requirements.txt

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,20 @@
2020
# ======= Sphinx =======
2121

2222
# Core Sphinx
23-
sphinx>=5.3,<7 # Extended version range for Python 3.12
24-
docutils>=0.18 # More flexible version requirement
23+
sphinx<8.0
24+
docutils<0.20
2525
Pillow # Required for imghdr module
2626
setuptools # Required for pkg_resources
2727

2828
# Theme
29-
sphinx-book-theme>=0.4.0 # Book theme (primary)
30-
sphinx_rtd_theme>=1.0 # Read the Docs theme (alternative)
29+
sphinx-book-theme
30+
sphinx-design
3131

3232
# Extensions used in conf.py
3333
breathe>=4.30 # C++ documentation integration
34-
myst-parser[linkify]>=0.14.0 # Markdown support
35-
sphinx-design # UI components
36-
sphinx-copybutton>=0.5.0 # Copy button for code blocks
34+
myst-parser
35+
sphinx-rtd-theme
36+
sphinx-copybutton
3737
sphinxcontrib-bibtex>=2.5 # Bibliography support
3838
sphinxcontrib-katex # Math rendering
3939
standard-imghdr
@@ -48,4 +48,36 @@ sphinx-hoverxref>=0.3b1 # Hover tooltips for references
4848
# Documentation Dependencies
4949
sphinxcontrib-matlabdomain>=0.22.0,<0.23.0 # Pin version for Python 3.12 compatibility
5050
sphinxcontrib-serializinghtml
51-
sphinxcontrib-htmlhelp
51+
sphinxcontrib-htmlhelp
52+
53+
# Core documentation packages
54+
sphinx==6.2.1
55+
sphinx-book-theme==1.1.4
56+
myst-parser==3.0.1
57+
breathe==4.35.0
58+
59+
# Theme enhancements
60+
sphinx-design==0.6.1
61+
sphinx-copybutton==0.5.2
62+
sphinx-togglebutton==0.3.2
63+
64+
# Additional features
65+
sphinxcontrib-bibtex==2.5.0
66+
sphinxcontrib-katex==0.9.10
67+
sphinxcontrib-jquery==4.1
68+
69+
# MATLAB domain
70+
sphinxcontrib-matlabdomain==0.22.1
71+
72+
# Image handling
73+
Pillow==11.2.1
74+
75+
# HTTP requests for contributors extension
76+
requests==2.32.3
77+
78+
# For PDF generation
79+
latexcodec==3.0.0
80+
pygments==2.19.1
81+
82+
# Development tools
83+
jinja2==3.1.6
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""
2+
Sphinx extension to generate a sitemap.xml for the HTML website.
3+
"""
4+
5+
import os
6+
from datetime import datetime
7+
from pathlib import Path
8+
from jinja2 import Environment, FileSystemLoader
9+
10+
def generate_sitemap(app, exception):
11+
"""
12+
Generate sitemap.xml in the HTML build directory.
13+
"""
14+
if exception is not None or app.builder.name != "html":
15+
return
16+
17+
try:
18+
# Get configuration variables
19+
sitemap_url_base = app.config.html_baseurl or "https://mole-pdes.readthedocs.io"
20+
if not sitemap_url_base.endswith("/"):
21+
sitemap_url_base += "/"
22+
23+
# Get template directory and set up jinja2 environment
24+
template_dir = os.path.join(app.srcdir, "_templates")
25+
env = Environment(loader=FileSystemLoader(template_dir))
26+
template = env.get_template("sitemap.xml")
27+
28+
# Build a list of all HTML files in the build directory
29+
html_dir = app.outdir
30+
build_path = Path(html_dir)
31+
32+
# Create a list of pages for the sitemap
33+
pages = []
34+
35+
for path in build_path.rglob("*.html"):
36+
# Get the relative path from the build directory
37+
rel_path = path.relative_to(build_path)
38+
url = sitemap_url_base + str(rel_path).replace(os.sep, "/")
39+
40+
# Get the last modification time of the file
41+
last_mod = datetime.fromtimestamp(path.stat().st_mtime).strftime("%Y-%m-%d")
42+
43+
# Skip some files that shouldn't be in the sitemap
44+
if any(part.startswith("_") for part in rel_path.parts):
45+
continue
46+
47+
# Add the page to the list
48+
pages.append({
49+
"loc": url,
50+
"lastmod": last_mod
51+
})
52+
53+
# Render the sitemap
54+
sitemap_content = template.render(pages=pages)
55+
56+
# Write the sitemap to the build directory
57+
sitemap_path = os.path.join(html_dir, "sitemap.xml")
58+
with open(sitemap_path, "w") as f:
59+
f.write(sitemap_content)
60+
61+
print(f"Generated sitemap.xml with {len(pages)} pages")
62+
63+
except Exception as e:
64+
print(f"Error generating sitemap.xml: {e}")
65+
66+
def setup(app):
67+
"""
68+
Set up the Sphinx extension.
69+
"""
70+
# Run after the HTML build is finished
71+
app.connect("build-finished", generate_sitemap)
72+
73+
return {
74+
"version": "0.1",
75+
"parallel_read_safe": True,
76+
"parallel_write_safe": True,
77+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
Sphinx extension to display GitHub contributors.
3+
"""
4+
5+
import os
6+
import json
7+
import requests
8+
from docutils import nodes
9+
from docutils.parsers.rst import Directive, directives
10+
11+
12+
class GithubContributorsDirective(Directive):
13+
"""
14+
Directive to display GitHub contributors.
15+
16+
Usage:
17+
.. github-contributors:: owner/repo
18+
:max: 10
19+
:columns: 4
20+
"""
21+
22+
required_arguments = 1
23+
optional_arguments = 0
24+
option_spec = {
25+
'max': directives.positive_int,
26+
'columns': directives.positive_int,
27+
'exclude': directives.unchanged,
28+
}
29+
has_content = False
30+
31+
def run(self):
32+
# Parse arguments
33+
repo_path = self.arguments[0]
34+
max_contributors = self.options.get('max', 100)
35+
columns = self.options.get('columns', 4)
36+
exclude_logins = self.options.get('exclude', '').split(',')
37+
exclude_logins = [login.strip() for login in exclude_logins if login.strip()]
38+
39+
try:
40+
# Use GitHub API to get contributors
41+
api_url = f"https://api.github.com/repos/{repo_path}/contributors?per_page={max_contributors}"
42+
43+
# Check for GitHub token in environment variables
44+
github_token = os.environ.get('GITHUB_TOKEN', '')
45+
headers = {}
46+
if github_token:
47+
headers['Authorization'] = f'token {github_token}'
48+
49+
response = requests.get(api_url, headers=headers)
50+
response.raise_for_status()
51+
contributors = response.json()
52+
53+
# Filter excluded contributors
54+
contributors = [c for c in contributors if c['login'] not in exclude_logins]
55+
56+
# Create a container div for the contributors grid
57+
container = nodes.container(classes=['contributors-grid'])
58+
container.append(nodes.raw('',
59+
f'<style>.contributors-grid {{ display: grid; grid-template-columns: repeat({columns}, 1fr); gap: 20px; }}</style>',
60+
format='html'))
61+
62+
# Add each contributor
63+
for contributor in contributors[:max_contributors]:
64+
login = contributor['login']
65+
avatar_url = contributor['avatar_url']
66+
profile_url = contributor['html_url']
67+
contributions = contributor['contributions']
68+
69+
# Create a container for this contributor
70+
contributor_div = nodes.container(classes=['contributor'])
71+
contributor_div.append(nodes.raw('',
72+
f'''
73+
<div style="text-align: center; margin-bottom: 10px;">
74+
<a href="{profile_url}" target="_blank" style="text-decoration: none;">
75+
<img src="{avatar_url}" alt="{login}" style="width: 80px; height: 80px; border-radius: 50%; margin-bottom: 5px;" />
76+
<div>{login}</div>
77+
<div style="font-size: 0.8em; color: #666;">{contributions} commit{"s" if contributions != 1 else ""}</div>
78+
</a>
79+
</div>
80+
''',
81+
format='html'))
82+
83+
container.append(contributor_div)
84+
85+
return [container]
86+
87+
except Exception as e:
88+
warning_node = nodes.warning()
89+
warning_node += nodes.paragraph(text=f"Error getting GitHub contributors: {e}")
90+
return [warning_node]
91+
92+
93+
def setup(app):
94+
"""
95+
Set up the Sphinx extension.
96+
"""
97+
app.add_directive('github-contributors', GithubContributorsDirective)
98+
99+
return {
100+
'version': '0.1',
101+
'parallel_read_safe': True,
102+
'parallel_write_safe': True,
103+
}

0 commit comments

Comments
 (0)