diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml
index 0e35de3ce09..8f749fed029 100644
--- a/.github/workflows/doc-build.yml
+++ b/.github/workflows/doc-build.yml
@@ -132,25 +132,35 @@ jobs:
git config --global --add safe.directory $(pwd)
git config --global user.email "ci-sage@example.com"
git config --global user.name "Build documentation workflow"
- # mathjax path in old doc (regex)
- mathjax_path_from="[-./A-Za-z_]*/tex-chtml[.]js?v=[0-9a-f]*"
- # mathjax path in new doc
- mathjax_path_to=$(docker exec -e SAGE_USE_CDNS=yes BUILD /sage/sage -python -c "from sage_docbuild.conf import mathjax_path; print(mathjax_path)")
- new_version=$(docker exec BUILD cat src/VERSION.txt)
- mkdir -p doc/
- docker cp BUILD:/sage/local/share/doc/sage/html doc/
- # Wipe out chronic diffs between old doc and new doc
- (cd doc && \
- find . -name "*.html" | xargs sed -i -e '/class="sidebar-brand-text"/ s/Sage [0-9a-z.]* /Sage '"$new_version"' /' \
- -e '/;,\;; d')
- # Create git repo from old doc
- (cd doc && \
- git init && \
- (echo "*.svg binary"; echo "*.pdf binary") >> .gitattributes && \
- (echo ".buildinfo"; echo '*.inv'; echo '.git*'; echo '*.svg'; echo '*.pdf'; echo '*.png'; echo 'searchindex.js') > .gitignore; \
- git add -A && git commit --quiet -m 'old')
+ # Check if we are on PR
+ PR_NUMBER=""
+ if [[ -n "$GITHUB_REF" ]]; then
+ if [[ "$GITHUB_REF" =~ refs/pull/([0-9]+)/merge ]]; then
+ PR_NUMBER="${BASH_REMATCH[1]}"
+ fi
+ fi
+ # If so, then prepare to create CHANGES.html
+ if [[ -n "$PR_NUMBER" ]]; then
+ # mathjax path in old doc (regex)
+ mathjax_path_from="[-./A-Za-z_]*/tex-chtml[.]js?v=[0-9a-f]*"
+ # mathjax path in new doc
+ mathjax_path_to=$(docker exec -e SAGE_USE_CDNS=yes BUILD /sage/sage -python -c "from sage_docbuild.conf import mathjax_path; print(mathjax_path)")
+ new_version=$(docker exec BUILD cat src/VERSION.txt)
+ mkdir -p doc/
+ docker cp BUILD:/sage/local/share/doc/sage/html doc/
+ # Wipe out chronic diffs between old doc and new doc
+ (cd doc && \
+ find . -name "*.html" | xargs sed -i -e '/class="sidebar-brand-text"/ s/Sage [0-9a-z.]* /Sage '"$new_version"' /' \
+ -e '/;,\;; d')
+ # Create git repo from old doc
+ (cd doc && \
+ git init && \
+ (echo "*.svg binary"; echo "*.pdf binary") >> .gitattributes && \
+ (echo ".buildinfo"; echo '*.inv'; echo '.git*'; echo '*.svg'; echo '*.pdf'; echo '*.png'; echo 'searchindex.js') > .gitignore; \
+ git add -A && git commit --quiet -m 'old')
+ fi
- name: Build doc
id: docbuild
@@ -174,21 +184,31 @@ jobs:
# We copy everything to a local folder
docker cp --follow-link BUILD:/sage/local/share/doc/sage/html doc
docker cp --follow-link BUILD:/sage/local/share/doc/sage/index.html doc
- (cd doc && git commit -a -m 'new')
- # Wipe out chronic diffs of new doc against old doc before creating CHANGES.html
- (cd doc && \
- find . -name "*.html" | xargs sed -i -e '/This is documentation for/ s/ built with GitHub PR .*. Doc/. Doc/' \
- -e '/;,\;; d' \
- && git commit -a -m 'wipe-out')
- # Since HEAD is at commit 'wipe-out', HEAD~1 is commit 'new' (new doc), HEAD~2 is commit 'old' (old doc)
- .ci/create-changes-html.sh $(cd doc && git rev-parse HEAD~2) doc
- # Restore the new doc with changes made in create-changes-html.sh but dropping changes by "wipe out"
- (cd doc && git stash -q && git checkout -q -f HEAD~1 && git stash pop -q)
- # Sometimes rm -rf .git errors out because of some diehard hidden files
- # So we simply move it out of the doc directory
- (cd doc && mv .git ../git && mv .gitattributes ../gitattributes)
- mv CHANGES.html doc
+ # Check if we are on PR
+ PR_NUMBER=""
+ if [[ -n "$GITHUB_REF" ]]; then
+ if [[ "$GITHUB_REF" =~ refs/pull/([0-9]+)/merge ]]; then
+ PR_NUMBER="${BASH_REMATCH[1]}"
+ fi
+ fi
+ # If so, then create CHANGES.html
+ if [[ -n "$PR_NUMBER" ]]; then
+ (cd doc && git commit -a -m 'new')
+ # Wipe out chronic diffs of new doc against old doc before creating CHANGES.html
+ (cd doc && \
+ find . -name "*.html" | xargs sed -i -e '/This is documentation of/ s/ built with GitHub PR .* for development/ for development/' \
+ -e '/;,\;; d' \
+ && git commit -a -m 'wipe-out')
+ # Since HEAD is at commit 'wipe-out', HEAD~1 is commit 'new' (new doc), HEAD~2 is commit 'old' (old doc)
+ .ci/create-changes-html.sh $(cd doc && git rev-parse HEAD~2) doc
+ # Restore the new doc with changes made in create-changes-html.sh but dropping changes by "wipe out"
+ (cd doc && git stash -q && git checkout -q -f HEAD~1 && git stash pop -q)
+ # Sometimes rm -rf .git errors out because of some diehard hidden files
+ # So we simply move it out of the doc directory
+ (cd doc && mv .git ../git && mv .gitattributes ../gitattributes)
+ mv CHANGES.html doc
+ fi
# Zip everything for increased performance
zip -r doc.zip doc
@@ -228,7 +248,7 @@ jobs:
# We copy everything to a local folder
docker cp --follow-link BUILD:/sage/local/share/doc/sage/html livedoc
docker cp --follow-link BUILD:/sage/local/share/doc/sage/pdf livedoc
- docker cp BUILD:/sage/local/share/doc/sage/index.html livedoc
+ docker cp --follow-link BUILD:/sage/local/share/doc/sage/index.html livedoc
zip -r livedoc.zip livedoc
- name: Upload live doc
diff --git a/.github/workflows/doc-publish.yml b/.github/workflows/doc-publish.yml
index d5eafc953e1..5454519a6fb 100644
--- a/.github/workflows/doc-publish.yml
+++ b/.github/workflows/doc-publish.yml
@@ -129,6 +129,16 @@ jobs:
run: unzip livedoc.zip -d livedoc
if: steps.download-doc.outcome == 'success'
+ - name: Create _headers file for permissive CORS
+ run: |
+ cat < livedoc/livedoc/_headers
+ /*
+ Access-Control-Allow-Origin: *
+ Access-Control-Allow-Methods: GET
+ Access-Control-Allow-Headers: Content-Type
+ EOF
+ if: steps.download-doc.outcome == 'success'
+
- name: Deploy to netlify with doc-TAG alias
id: deploy-netlify
uses: netlify/actions/cli@master
diff --git a/src/bin/sage-update-version b/src/bin/sage-update-version
index c6680918a36..179461a8340 100755
--- a/src/bin/sage-update-version
+++ b/src/bin/sage-update-version
@@ -112,6 +112,25 @@ export SAGE_VERSION
export SAGE_RELEASE_DATE
envsubst <"$SAGE_ROOT/CITATION.cff.in" >"$SAGE_ROOT/CITATION.cff"
+# Update src/doc/versions.txt file storing the URLs of the docs for the recent stable releases
+if [[ $SAGE_VERSION =~ ^[0-9]+(\.[0-9]+)*$ ]]; then
+ file_path="$SAGE_ROOT/src/doc/en/website/versions.txt"
+ # Extract the most recent version line from versions.txt
+ line_number=$(grep -n '^[0-9]' "$file_path" | head -n 1 | cut -d: -f1)
+ version_line=$(sed -n "${line_number}p" "$file_path")
+ domain=${version_line#*--}
+ version=${SAGE_VERSION//./-}
+ # For the origin of this format, see .github/workflows/doc-publish.yml
+ url="doc-$version--$domain"
+ # Add new line to versions.txt
+ sed -i "${line_number}i $SAGE_VERSION $url" "$file_path"
+ # If the number of version lines is more than 10, remove the last line
+ line_count=$(grep -c '^[0-9]' "$file_path")
+ if [ "$line_count" -gt 10 ]; then
+ sed -i '$ d' "$file_path"
+ fi
+fi
+
# Commit auto-generated changes
git commit -m "Updated SageMath version to $SAGE_VERSION" -- \
"$SAGE_ROOT/VERSION.txt" \
@@ -124,6 +143,7 @@ git commit -m "Updated SageMath version to $SAGE_VERSION" -- \
"$SAGE_ROOT/build/pkgs/*/version_requirements.txt" \
"$SAGE_ROOT"/pkgs/*/VERSION.txt \
"$SAGE_ROOT/.upstream.d/20-github.com-sagemath-sage-releases" \
+ "$SAGE_ROOT/src/doc/en/website/versions.txt" \
|| die "Error committing to the repository."
git tag -a "$SAGE_VERSION" -m "$SAGE_VERSION_BANNER" \
diff --git a/src/doc/common/static/custom-furo.css b/src/doc/common/static/custom-furo.css
index ae75c2b6383..85d2a0776cf 100644
--- a/src/doc/common/static/custom-furo.css
+++ b/src/doc/common/static/custom-furo.css
@@ -24,14 +24,53 @@ a.pdf:hover {
/* Style for announcement banner */
.announcement {
- background: orange;
+ background: orange;
}
.announcement-content {
- color: black;
+ color: black;
}
.announcement-content a {
- color: white;
- text-decoration: none;
+ color: white;
+ text-decoration: none;
}
+
+/* Style for the floating versions menu */
+
+.sidebar-drawer {
+ z-index: 100;
+}
+
+.select-wrapper {
+ display: block;
+ text-align: center;
+}
+
+#versions-menu {
+ display: inline-block;
+ font-size: small;
+ opacity: 1;
+ border: none;
+ background-color: transparent;
+ color: var(--color-sidebar-link-text--top-level);;
+}
+
+#versions-menu:focus {
+ outline: none;
+}
+
+body[data-theme="dark"] {
+ #versions-menu {
+ background-color: transparent;
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ body[data-theme="auto"] { /* the same styles with body[data-theme="dark"] */
+ #versions-menu {
+ background-color: transparent;
+ }
+ }
+}
+
diff --git a/src/doc/common/static/custom-jupyter-sphinx.css b/src/doc/common/static/custom-jupyter-sphinx.css
index 7e090b5e08d..482a2ddaed2 100644
--- a/src/doc/common/static/custom-jupyter-sphinx.css
+++ b/src/doc/common/static/custom-jupyter-sphinx.css
@@ -96,41 +96,41 @@ body[data-theme="dark"] {
}
@media (prefers-color-scheme: dark) {
- body[data-theme="auto"] { /* the same styles with body[data-theme="dark"] */
- .jupyter_container {
- color: white;
- background-color: black;
- }
-
- .jupyter_container .highlight {
- background-color: black;
- }
-
- .thebelab-button {
- color: #d0d0d0;
- background-color: #383838;
- }
-
- .thebelab-button:active {
- color: #368ce2;
- }
-
- #thebelab-activate-button {
- background-color: #383838;
- }
-
- #thebelab-activate-button:active {
- color: #368ce2;
- }
-
- .thebelab-cell .jp-OutputArea-output {
- color: white;
- background-color: black;
- }
-
- .thebelab-cell .jp-OutputArea-output pre {
- color: white;
- background-color: black;
+ body[data-theme="auto"] { /* the same styles with body[data-theme="dark"] */
+ .jupyter_container {
+ color: white;
+ background-color: black;
+ }
+
+ .jupyter_container .highlight {
+ background-color: black;
+ }
+
+ .thebelab-button {
+ color: #d0d0d0;
+ background-color: #383838;
+ }
+
+ .thebelab-button:active {
+ color: #368ce2;
+ }
+
+ #thebelab-activate-button {
+ background-color: #383838;
+ }
+
+ #thebelab-activate-button:active {
+ color: #368ce2;
+ }
+
+ .thebelab-cell .jp-OutputArea-output {
+ color: white;
+ background-color: black;
+ }
+
+ .thebelab-cell .jp-OutputArea-output pre {
+ color: white;
+ background-color: black;
+ }
}
- }
}
diff --git a/src/doc/common/static/jupyter-sphinx-furo.js b/src/doc/common/static/jupyter-sphinx-furo.js
index a43a7c8b122..b63de354db8 100644
--- a/src/doc/common/static/jupyter-sphinx-furo.js
+++ b/src/doc/common/static/jupyter-sphinx-furo.js
@@ -58,6 +58,71 @@ const observer2 = new MutationObserver(callback);
observer2.observe(document.getElementsByClassName("content")[0], { childList: true, subtree: true });
+//
+// Version selector
+//
+
+function fetchVersions() {
+ try {
+ let menu = document.getElementById('versions-menu');
+
+ // For the origin of the this site, see .github/workflows/doc-publish.yml
+ fetch('https://doc-release--sagemath.netlify.app/html/en/versions.txt')
+ .then(response => {
+ if (!response.ok) {
+ throw new Error('Network response was not ok ' + response.statusText);
+ }
+ return response.text();
+ })
+ .then(text => {
+ const lines = text.split('\n');
+ lines.forEach(line => {
+ if (!line.startsWith('#')) { // Ignore the comment line
+ let [ver, url] = line.split(' ');
+ if (ver && url) {
+ if (!url.startsWith('https://')) {
+ url = 'https://' + url;
+ }
+ let option = document.createElement('option');
+ option.value = url;
+ option.text = ver;
+ menu.add(option);
+ }
+ }
+ });
+ })
+ .catch(error => {
+ console.log('Failed to fetch versions.txt file.');
+ });
+ } catch (error) {
+ console.log('Failed to fetch versions.txt file.');
+ }
+}
+
+fetchVersions()
+
+// Function to change the version based on versions menu selection
+function changeVersion() {
+ let select_element = document.getElementById("versions-menu");
+ let selected_ver = select_element.options[select_element.selectedIndex].text;
+ let selected_url = select_element.value;
+ if (selected_url) {
+ if (window.location.protocol == 'file:') {
+ let pathname = window.location.pathname;
+ let cutoff_point = pathname.indexOf('doc/sage');
+ if (cutoff_point !== -1) {
+ pathname = pathname.substring(cutoff_point + 8);
+ window.location.href = selected_url + pathname;
+ } else {
+ window.location.href = selected_url + 'html/en/index.html';
+ }
+ } else {
+ window.location.href = selected_url + window.location.pathname;
+ }
+ }
+}
+
+
// Listen to the kernel status changes
// https://thebe.readthedocs.io/en/stable/events.html
thebelab.on("status", function (evt, data) {
diff --git a/src/doc/common/templates-furo/sidebar/version-selector.html b/src/doc/common/templates-furo/sidebar/version-selector.html
new file mode 100644
index 00000000000..6807b131dcf
--- /dev/null
+++ b/src/doc/common/templates-furo/sidebar/version-selector.html
@@ -0,0 +1,6 @@
+
+
+
diff --git a/src/doc/en/website/versions.txt b/src/doc/en/website/versions.txt
new file mode 100644
index 00000000000..0fcb63d686a
--- /dev/null
+++ b/src/doc/en/website/versions.txt
@@ -0,0 +1,16 @@
+# This file is used by the version selector of the Sage doc
+# and updated by the script src/bin/sage-update-version
+#
+# The lines are for recent stable releases (at most 10 lines)
+# A line consists of the version and the URL to the doc
+#
+# The sage-update-version script adds a new line for a new stable release
+# when run by the Sage release manager to prepare a new release
+#
+10.3 doc-10-3--sagemath.netlify.app
+10.2 doc-10-2--sagemath.netlify.app
+10.1 doc-10-1--sagemath.netlify.app
+10.0 doc-10-0--sagemath.netlify.app
+9.8 doc-9-8--sagemath.netlify.app
+9.7 doc-9-7--sagemath.netlify.app
+9.6 doc-9-6--sagemath.netlify.app
diff --git a/src/sage_docbuild/builders.py b/src/sage_docbuild/builders.py
index 967616e2e28..871cc4705a2 100644
--- a/src/sage_docbuild/builders.py
+++ b/src/sage_docbuild/builders.py
@@ -442,6 +442,11 @@ def html(self):
"""
super().html()
html_output_dir = self._output_dir('html')
+
+ # This file is used by src/doc/common/static/jupyter-sphinx-furo.js
+ # for doc version selector
+ shutil.copy2(os.path.join(self.dir, 'versions.txt'), html_output_dir)
+
for f in os.listdir(html_output_dir):
src = os.path.join(html_output_dir, f)
dst = os.path.join(html_output_dir, '..', f)
@@ -451,9 +456,8 @@ def html(self):
else:
shutil.copy2(src, dst)
- root_index_file = os.path.join(html_output_dir, '../../../index.html')
- shutil.copy2(os.path.join(SAGE_DOC_SRC, self.lang, 'website', 'root_index.html'),
- root_index_file)
+ shutil.copy2(os.path.join(self.dir, 'root_index.html'),
+ os.path.join(html_output_dir, '../../../index.html'))
def pdf(self):
"""
diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py
index 9ff67bc9ca7..50d56ccfd05 100644
--- a/src/sage_docbuild/conf.py
+++ b/src/sage_docbuild/conf.py
@@ -441,26 +441,27 @@ def linkcode_resolve(domain, info):
# "source_directory" is defined in conf.py customized for the doc
}
-if not version.split('.')[-1].isnumeric(): # develop version
+# Check the condition for announcement banner
+github_ref = os.environ.get('GITHUB_REF', '')
+if github_ref:
+ match = re.search(r'refs/pull/(\d+)/merge', github_ref)
+ if match:
+ pr_number = match.group(1)
+is_develop_version = not version.split('.')[-1].isnumeric()
+is_for_github_pr = github_ref and match and pr_number
+
+if is_develop_version or is_for_github_pr: # condition for announcement banner
# This URL is hardcoded in the file .github/workflows/doc-publish.yml.
# See NETLIFY_ALIAS of the "Deploy to Netlify" step.
ver = f'{version}'
- github_ref = os.environ.get('GITHUB_REF', '')
- if github_ref:
- match = re.search(r'refs/pull/(\d+)/merge', github_ref)
- if match:
- # As this doc is built for a GitHub PR, we plant links
- # to the PR in the announcement banner.
- pr_number = match.group(1)
- pr_url = f'https://github.com/sagemath/sage/pull/{pr_number}'
- pr_sha = os.environ.get('PR_SHA', '')
- pr_commit = pr_url + f'/commits/{pr_sha}'
- ver += f' built with GitHub PR #{pr_number}' \
- f' on {pr_sha[:7]}' \
- f' [changes]'
- banner = f'This is documentation for Sage development version {ver}. ' \
- 'Documentation for the latest stable version is ' \
- 'here.'
+ if is_for_github_pr:
+ pr_url = f'https://github.com/sagemath/sage/pull/{pr_number}'
+ pr_sha = os.environ.get('PR_SHA', '')
+ pr_commit = pr_url + f'/commits/{pr_sha}'
+ ver += f' built with GitHub PR #{pr_number}' \
+ f' on {pr_sha[:7]}' \
+ f' [changes]'
+ banner = f'This is documentation for Sage version {ver} for development purpose.'
html_theme_options.update({ "announcement": banner })
# The name of the Pygments (syntax highlighting) style to use. This
@@ -473,6 +474,7 @@ def linkcode_resolve(domain, info):
"**": [
"sidebar/scroll-start.html",
"sidebar/brand.html",
+ "sidebar/version-selector.html",
"sidebar/search.html",
"sidebar/home.html",
"sidebar/navigation.html",
@@ -679,7 +681,7 @@ def add_page_context(app, pagename, templatename, context, doctree):
path2 = os.path.join(SAGE_DOC, 'html', 'en')
relpath = os.path.relpath(path2, path1)
context['release'] = release
- context['documentation_title'] = 'Sage {}'.format(release) + ' Documentation'
+ context['documentation_title'] = f'Version {release} Documentation'
context['documentation_root'] = os.path.join(relpath, 'index.html')
if 'website' in path1:
context['title'] = 'Documentation'
@@ -688,7 +690,7 @@ def add_page_context(app, pagename, templatename, context, doctree):
if 'reference' in path1 and not path1.endswith('reference'):
path2 = os.path.join(SAGE_DOC, 'html', 'en', 'reference')
relpath = os.path.relpath(path2, path1)
- context['reference_title'] = 'Sage {}'.format(release) + ' Reference Manual'
+ context['reference_title'] = f'Version {release} Reference Manual'
context['reference_root'] = os.path.join(relpath, 'index.html')
context['refsub'] = True
if pagename.startswith('sage/'):