Skip to content

Commit

Permalink
gh-36144: Revive sage live doc using jupyter-sphinx
Browse files Browse the repository at this point in the history
    
<!-- ^^^^^
Please provide a concise, informative and self-explanatory title.
Don't put issue numbers in there, do this in the PR body below.
For example, instead of "Fixes #1234" use "Introduce new method to
calculate 1+1"
-->
<!-- Describe your changes here in detail -->

We fix #33320 and  #24367.

We also upgrade jupyter-sphinx standard package to the latest version
0.4.0: https://github.com/jupyter/jupyter-sphinx/releases/tag/v0.4.0,
and patch it to facilitate live doc.

By default, live doc depends on https://github.com/sagemath/sage-binder-
env for binder (remote jupyter server).

To showcase the live doc, we edited "A Tour of Sage"  doc and some parts
of sage doc. Visit

- https://deploy-preview-36144--sagemath-
tobias.netlify.app/a_tour_of_sage or
- https://deploy-preview-36144--sagemath-
tobias.netlify.app/tutorial/latex  (edited to resolve #24367)

**Click the activate button to activate live doc** The button is hidden
to the right middle side of the browser. After clicked, the activate
button shows the status of the launching kernel.

**Be patient if the kernel loads slow.  It may take several minutes.**
Sometimes it may be stuck in the "launching" state. In that case, reload
the page and try again.

Note that `%display latex` is the default. You should use `%display
plain` to get plain textual output.

## Preparation

```
./sage -i jupyterlab
```

If things do not work as expected, first remove the existing
documentation by
```
make doc-clean doc-uninstall
```

## Build static doc

```
export SAGE_LIVE_DOC=no    # default
make -j4
```

## Build live doc with the default Binder repo

```
export SAGE_LIVE_DOC=yes
export SAGE_JUPYTER_SERVER=binder    # default
make -j4
```

## Build live doc with a custom Binder repo

```
export SAGE_LIVE_DOC=yes
export SAGE_JUPYTER_SERVER=binder:sagemath/sage-binder-env    # default
make -j4
```
The kernel is "sagemath".

## Build live doc with a local server

```
export SAGE_LIVE_DOC=yes
export SAGE_JUPYTER_SERVER=http://localhost:8889
export SAGE_JUPYTER_SERVER_TOKEN=secret              # default
make -j4
```
 and run a local jupyter server before browsing the documentation.
```
./sage --notebook=jupyterlab \
  --ServerApp.token='secret' \
  --ServerApp.allow_origin='null' \
  --ServerApp.disable_check_xsrf=true \
  --ServerApp.port=8889 \
  --ServerApp.open_browser=false
```

## Build live doc selectively

First build static doc

```
make -j4
```
and then build live doc selectively either by

```
./sage --docbuild --live-doc a_tour_of_sage html
```
or
```
export SAGE_JUPYTER_SERVER=http://localhost:8889
export SAGE_JUPYTER_SERVER_TOKEN=secret              # default
./sage --docbuild --live-doc a_tour_of_sage html
```
with a local server.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->
<!-- If your change requires a documentation PR, please link it
appropriately -->
<!-- If you're unsure about any of these, don't hesitate to ask. We're
here to help! -->
<!-- Feel free to remove irrelevant items. -->

- [x] The title is concise, informative, and self-explanatory.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [ ] I have created tests covering the changes.
- [x] I have updated the documentation accordingly.

Related upstream issues:
- jupyter/jupyter-sphinx#201
- jupyter/jupyter-sphinx#231
    
URL: #36144
Reported by: Kwankyu Lee
Reviewer(s): github-actions[bot], Kwankyu Lee, Matthias Köppe
  • Loading branch information
Release Manager committed Oct 29, 2023
2 parents 9468290 + 857741c commit c59ebbb
Show file tree
Hide file tree
Showing 22 changed files with 915 additions and 964 deletions.
6 changes: 3 additions & 3 deletions build/pkgs/jupyter_sphinx/checksums.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
tarball=jupyter_sphinx-VERSION.tar.gz
sha1=241f6dfcd3aae4f44f330e2ba76480011b382d3d
md5=e7ab370d9793be5b20bce5447ccbd45b
cksum=2021246952
sha1=fb2abdd5e35da0886b12d45a6373c4dbcc24b244
md5=130daa6be810976c9f8e30aa04011e50
cksum=2882523000
upstream_url=https://pypi.io/packages/source/j/jupyter_sphinx/jupyter_sphinx-VERSION.tar.gz
2 changes: 1 addition & 1 deletion build/pkgs/jupyter_sphinx/package-version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.2
0.4.0.p0
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
From 5bffbe38302c695123779f87300d84090b4bd118 Mon Sep 17 00:00:00 2001
From: Kwankyu Lee <[email protected]>
Date: Mon, 28 Aug 2023 00:18:59 +0900
Subject: [PATCH] Patch for sage live doc

---
jupyter_sphinx/__init__.py | 4 ++--
jupyter_sphinx/execute.py | 11 +++++++++++
2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/jupyter_sphinx/__init__.py b/jupyter_sphinx/__init__.py
index 34af884..b7ca8ee 100644
--- a/jupyter_sphinx/__init__.py
+++ b/jupyter_sphinx/__init__.py
@@ -31,7 +31,7 @@ from .thebelab import ThebeButton, ThebeButtonNode, ThebeOutputNode, ThebeSource
REQUIRE_URL_DEFAULT = (
"https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"
)
-THEBELAB_URL_DEFAULT = "https://unpkg.com/thebelab@^0.4.0"
+THEBELAB_URL_DEFAULT = "https://unpkg.com/thebe@latest/lib/index.js"

logger = logging.getLogger(__name__)

@@ -186,7 +186,7 @@ def setup(app):
app.add_config_value("jupyter_sphinx_embed_url", None, "html")

# thebelab config, can be either a filename or a dict
- app.add_config_value("jupyter_sphinx_thebelab_config", None, "html")
+ app.add_config_value("jupyter_sphinx_thebelab_config", None, "env")
app.add_config_value("jupyter_sphinx_thebelab_url", THEBELAB_URL_DEFAULT, "html")

# linenos config
diff --git a/jupyter_sphinx/execute.py b/jupyter_sphinx/execute.py
index 558a26b..de44455 100644
--- a/jupyter_sphinx/execute.py
+++ b/jupyter_sphinx/execute.py
@@ -152,6 +152,17 @@ class ExecuteJupyterCells(SphinxTransform):
kernel_name = default_kernel
file_name = next(default_names)

+ # Save time when jupyter notebook execution is not necessary
+ if not any(not "execute" in node or node["execute"] for node in nodes):
+ # mimics empty cell output for each node
+ for node in nodes:
+ source = node.children[0]
+ source.attributes["classes"].append("code_cell")
+ node.attributes["cm_language"] = kernel_name
+ node += CellOutputNode(classes=["cell_output"])
+ apply_styling(node, thebe_config)
+ continue
+
# Add empty placeholder cells for non-executed nodes so nodes
# and cells can be zipped and the provided input/output
# can be inserted later
--
2.42.0

42 changes: 42 additions & 0 deletions src/doc/common/static/custom-codemirror-monokai.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* from https://codemirror.net/5/theme/monokai.css */
/* Based on Sublime Text's Monokai theme */

.cm-s-monokai.CodeMirror { background: #272822; color: #f8f8f2; }
.cm-s-monokai div.CodeMirror-selected { background: #49483E; }
.cm-s-monokai .CodeMirror-line::selection, .cm-s-monokai .CodeMirror-line > span::selection, .cm-s-monokai .CodeMirror-line > span > span::selection { background: rgba(73, 72, 62, .99); }
.cm-s-monokai .CodeMirror-line::-moz-selection, .cm-s-monokai .CodeMirror-line > span::-moz-selection, .cm-s-monokai .CodeMirror-line > span > span::-moz-selection { background: rgba(73, 72, 62, .99); }
.cm-s-monokai .CodeMirror-gutters { background: #272822; border-right: 0px; }
.cm-s-monokai .CodeMirror-guttermarker { color: white; }
.cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; }
.cm-s-monokai .CodeMirror-linenumber { color: #d0d0d0; }
.cm-s-monokai .CodeMirror-cursor { border-left: 1px solid #f8f8f0; }

.cm-s-monokai span.cm-comment { color: #75715e; }
.cm-s-monokai span.cm-atom { color: #ae81ff; }
.cm-s-monokai span.cm-number { color: #ae81ff; }

.cm-s-monokai span.cm-comment.cm-attribute { color: #97b757; }
.cm-s-monokai span.cm-comment.cm-def { color: #bc9262; }
.cm-s-monokai span.cm-comment.cm-tag { color: #bc6283; }
.cm-s-monokai span.cm-comment.cm-type { color: #5998a6; }

.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; }
.cm-s-monokai span.cm-keyword { color: #f92672; }
.cm-s-monokai span.cm-builtin { color: #66d9ef; }
.cm-s-monokai span.cm-string { color: #e6db74; }

.cm-s-monokai span.cm-variable { color: #f8f8f2; }
.cm-s-monokai span.cm-variable-2 { color: #9effff; }
.cm-s-monokai span.cm-variable-3, .cm-s-monokai span.cm-type { color: #66d9ef; }
.cm-s-monokai span.cm-def { color: #fd971f; }
.cm-s-monokai span.cm-bracket { color: #f8f8f2; }
.cm-s-monokai span.cm-tag { color: #f92672; }
.cm-s-monokai span.cm-header { color: #ae81ff; }
.cm-s-monokai span.cm-link { color: #ae81ff; }
.cm-s-monokai span.cm-error { background: #f92672; color: #f8f8f0; }

.cm-s-monokai .CodeMirror-activeline-background { background: #373831; }
.cm-s-monokai .CodeMirror-matchingbracket {
text-decoration: underline;
color: white !important;
}
140 changes: 140 additions & 0 deletions src/doc/common/static/custom-jupyter-sphinx.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
div.jupyter_container {
margin: .5rem 0;
}

div.jupyter_container + div.jupyter_container {
margin: 0 0 .5rem 0;
}

div.jupyter_container div.cell_input pre {
margin: .5rem;
}

div.jupyter_container div.cell_output div.output {
margin: .5rem;
}

.thebelab-cell .jp-OutputArea {
margin: 0 .5rem;
}

.thebelab-cell .jp-OutputArea-output {
margin: 0 0 .5rem 0;
overflow-x: auto;
}

.thebelab-button {
margin: .5rem 0 .5rem .5rem;
padding: 0 .5rem;
min-width: 2rem;
}

.thebelab-button:active {
color: #0f0fff;
}

.thebelab-busy {
margin-left: .5rem;
}

#thebelab-activate-button {
position: fixed;
right: -5.1rem;
top: calc(50% - 1.5rem);
width: 6rem;
border-radius: 1rem;
transition-property: right;
transition-duration: 300ms;
transition-timing-function: ease-out;
z-index: 100;
}

#thebelab-activate-button:hover {
right: .4rem;
}

#thebelab-activate-button:active {
color: #0f0fff;
}

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;
}
}

@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;
}
}
}




114 changes: 114 additions & 0 deletions src/doc/common/static/jupyter-sphinx-furo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Change the editor theme according to the furo light/dark/auto mode
function changeTheme(editor, theme) {
if (theme === 'dark') {
editor.setOption('theme', 'monokai'); // the same with pygments dark style in conf.py
} else if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
editor.setOption('theme', 'monokai');
} else {
editor.setOption('theme', 'default');
}
}

// Change the editor theme of all CodeMirror cells
function changeThemeAll(theme) {
const querySet = document.querySelectorAll('.CodeMirror');
for (var i = 0; i < querySet.length; i++) {
changeTheme(querySet[i].CodeMirror, theme);
}
}

// Use the theme data of the body element set by setTheme function
// defined in https://github.com/pradyunsg/furo/blob/main/src/furo/assets/scripts/furo.js
const body = document.body;
const observer1 = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.attributeName === 'data-theme') {
const theme = body.dataset.theme;
changeThemeAll(theme);
}
}
});
observer1.observe(body, { attributes: true });


// In the furo auto mode, we watch prefers-color-scheme and use the theme data
// of the body element to change the CodeMirror editor theme
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)');

function handlePrefersColorSchemeChange(e) {
const theme = body.dataset.theme;
if (theme === 'auto') {
changeThemeAll(theme);
}
}

prefersDarkMode.addEventListener('change', handlePrefersColorSchemeChange);


// Change the editor theme of a new CodeMirror cell.
const callback = function(mutationsList, observer) {
for(const mutation of mutationsList) {
if (mutation.type === 'childList') {
const theme = body.dataset.theme;
for (const addedNode of mutation.addedNodes) {
if (addedNode.classList && addedNode.classList.contains('CodeMirror')) {
changeTheme(addedNode.CodeMirror, theme);
}}}}};
const observer2 = new MutationObserver(callback);
observer2.observe(document.getElementsByClassName("content")[0], { childList: true, subtree: true });


// Listen to the kernel status changes
// https://thebe.readthedocs.io/en/stable/events.html
thebelab.on("status", function (evt, data) {
if (data.status === 'building') {
const elements = document.querySelectorAll('.thebelab-cell');
elements.forEach(element => {
element.style.filter = 'opacity(50%)';
});
const element = document.getElementById("thebelab-activate-button");
element.innerHTML = "Building";
element.style.right = '.4rem';
}
else if (data.status === 'built') {
const elements = document.querySelectorAll('.thebelab-cell');
elements.forEach(element => {
element.style.filter = 'opacity(60%)';
});
const element = document.getElementById("thebelab-activate-button");
element.innerHTML = "Built";
element.style.right = '.4rem';
}
else if (data.status === 'launching') {
const elements = document.querySelectorAll('.thebelab-cell');
elements.forEach(element => {
element.style.filter = 'opacity(70%)';
});
const element = document.getElementById("thebelab-activate-button");
element.innerHTML = "Launching";
element.style.right = '.4rem';
}
else if (data.status === 'failed') {
const elements = document.querySelectorAll('.thebelab-cell');
elements.forEach(element => {
element.style.filter = 'opacity(50%)';
});
const element = document.getElementById("thebelab-activate-button");
element.innerHTML = 'Failed: ' + data.message;
element.style.right = '.4rem';
element.style.width = 'auto';
element.style.color = 'red';
}
else if (data.status === 'ready') {
const elements = document.querySelectorAll('.thebelab-cell');
elements.forEach(element => {
element.style.filter = 'opacity(100%)';
});
const element = document.getElementById("thebelab-activate-button");
element.innerHTML = "Ready";
element.style.right = null;
// Run custom code when the kernel is ready
const kernel = data.kernel;
kernel.requestExecute({code: "%display latex"});
}
});
Loading

0 comments on commit c59ebbb

Please sign in to comment.