Skip to content
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ Tag | Description
`remove-input` | Remove the code cell input/source from the rendered output.
`remove-output` | Remove the code cell output from the rendered output.
`remove-stderr` | Remove the code cell output stderr from the rendered output.
`scroll-output` | Make the cell output scrollable if it is too long.
`scroll-input` | Make the cell input scrollable if it is too long.

Additionally, for code execution, these tags are provided (via `nbclient`):

Expand Down
14 changes: 14 additions & 0 deletions myst_nb/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,20 @@ def __post_init__(self):
},
)

scroll_outputs: bool = dc.field(
default=False,
metadata={
"validator": instance_of(bool),
"help": "Make long cell outputs scrollable",
"sections": (
Section.global_lvl,
Section.file_lvl,
Section.cell_lvl,
Section.render,
),
},
)

code_prompt_show: str = dc.field(
default="Show code cell {type}",
metadata={
Expand Down
10 changes: 10 additions & 0 deletions myst_nb/core/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ def render_nb_cell_code(self: SelfType, token: SyntaxTreeNode) -> None:
for tag in tags:
classes.append(f"tag_{tag.replace(' ', '_')}")

# handle config option for scrollable outputs
scroll_outputs = self.get_cell_level_config(
"scroll_outputs", token.meta["metadata"], line=cell_line
)
if scroll_outputs and not any( # don't override cell tags
tag in ["scroll-output", "output_scroll"] for tag in tags
):
# add the class defined in mystnb.css for scroll_outputs config option
classes.append("config_scroll_outputs")

# TODO do we need this -/_ duplication of tag names, or can we deprecate one?
hide_cell = "hide-cell" in tags
remove_input = (
Expand Down
97 changes: 52 additions & 45 deletions myst_nb/static/mystnb.css
Original file line number Diff line number Diff line change
Expand Up @@ -281,65 +281,72 @@ tbody span.pasted-inline img {
*
* It was before in https://github.com/executablebooks/sphinx-book-theme/blob/eb1b6baf098b27605e8f2b7b2979b17ebf1b9540/src/sphinx_book_theme/assets/styles/extensions/_myst-nb.scss
*/

div.cell.tag_output_scroll div.cell_output, div.cell.tag_scroll-output div.cell_output {
max-height: 24em;
overflow-y: auto;
max-width: 100%;
overflow-x: auto;
}

div.cell.tag_output_scroll div.cell_output::-webkit-scrollbar, div.cell.tag_scroll-output div.cell_output::-webkit-scrollbar {
width: var(--mystnb-scrollbar-width);
height: var(--mystnb-scrollbar-height);
}

div.cell.tag_output_scroll div.cell_output::-webkit-scrollbar-thumb, div.cell.tag_scroll-output div.cell_output::-webkit-scrollbar-thumb {
background: var(--mystnb-scrollbar-thumb-color);
border-radius: var(--mystnb-scrollbar-thumb-border-radius);
}

div.cell.tag_output_scroll div.cell_output::-webkit-scrollbar-thumb:hover, div.cell.tag_scroll-output div.cell_output::-webkit-scrollbar-thumb:hover {
background: var(--mystnb-scrollbar-thumb-hover-color);
}

@media print {
div.cell.tag_output_scroll div.cell_output, div.cell.tag_scroll-output div.cell_output {
max-height: unset;
overflow-y: visible;
max-width: unset;
overflow-x: visible;
}
div.cell:is(
.tag_output_scroll,
.tag_scroll-output,
.config_scroll_outputs
)
div.cell_output,
div.cell.tag_scroll-input div.cell_input {
max-height: 24em;
overflow-y: auto;
max-width: 100%;
overflow-x: auto;
Comment on lines +284 to +294
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! I forgot to mention: this is a little bit tangential to this PR. I realized the style definitions were redundant so I combined input and output styles. And CSS selector was too long so I simplified it with :is()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All I could do is to trust you and the test suite and downstream test PRs on this 😅

}

div.cell.tag_scroll-input div.cell_input {
max-height: 24em;
overflow-y: auto;
max-width: 100%;
overflow-x: auto;
div.cell.config_scroll_outputs div.cell_output:has(img) {
/* If the output cell has image(s), allow it to take 90% of viewport height
but still bounded between 24em and 60em */
max-height: clamp(24em, 90vh, 60em);
}

/* Custom scrollbars */
div.cell:is(
.tag_output_scroll,
.tag_scroll-output,
.config_scroll_outputs
)
div.cell_output::-webkit-scrollbar,
div.cell.tag_scroll-input div.cell_input::-webkit-scrollbar {
width: var(--mystnb-scrollbar-width);
height: var(--mystnb-scrollbar-height);
width: var(--mystnb-scrollbar-width);
height: var(--mystnb-scrollbar-height);
}

div.cell:is(
.tag_output_scroll,
.tag_scroll-output,
.config_scroll_outputs
)
div.cell_output::-webkit-scrollbar-thumb,
div.cell.tag_scroll-input div.cell_input::-webkit-scrollbar-thumb {
background: var(--mystnb-scrollbar-thumb-color);
border-radius: var(--mystnb-scrollbar-thumb-border-radius);
background: var(--mystnb-scrollbar-thumb-color);
border-radius: var(--mystnb-scrollbar-thumb-border-radius);
}

div.cell:is(
.tag_output_scroll,
.tag_scroll-output,
.config_scroll_outputs
)
div.cell_output::-webkit-scrollbar-thumb:hover,
div.cell.tag_scroll-input div.cell_input::-webkit-scrollbar-thumb:hover {
background: var(--mystnb-scrollbar-thumb-hover-color);
background: var(--mystnb-scrollbar-thumb-hover-color);
}

/* In print mode, unset scroll styles */
@media print {
div.cell.tag_scroll-input div.cell_input {
max-height: unset;
overflow-y: visible;
max-width: unset;
overflow-x: visible;
}
div.cell:is(
.tag_output_scroll,
.tag_scroll-output,
.config_scroll_outputs
)
div.cell_output,
div.cell.tag_scroll-input div.cell_input {
max-height: unset;
overflow-y: visible;
max-width: unset;
overflow-x: visible;
}
}

/* Font colors for translated ANSI escape sequences
Expand Down
171 changes: 171 additions & 0 deletions tests/notebooks/scroll_outputs.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "356e9205",
"metadata": {},
"source": [
"# Scroll long outputs"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "cd1b32ee",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"short output\n",
"short output\n"
]
}
],
"source": [
"for i in range(2):\n",
" print(\"short output\")"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "91a31d3f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n",
"long output\n"
]
}
],
"source": [
"for i in range(100):\n",
" print(\"long output\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "myst-nb-py311",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
11 changes: 11 additions & 0 deletions tests/test_render_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,14 @@ def test_hide_cell_content(sphinx_run, file_regression):
assert sphinx_run.warnings() == ""
doctree = sphinx_run.get_resolved_doctree("hide_cell_content")
file_regression.check(doctree.pformat(), extension=".xml", encoding="utf-8")


@pytest.mark.sphinx_params(
"scroll_outputs.ipynb", conf={"nb_execution_mode": "off", "nb_scroll_outputs": True}
)
def test_scroll_outputs(sphinx_run, file_regression):
"""Test that scrollable outputs are rendered correctly."""
sphinx_run.build()
assert sphinx_run.warnings() == ""
doctree = sphinx_run.get_resolved_doctree("scroll_outputs")
file_regression.check(doctree.pformat(), extension=".xml", encoding="utf-8")
Loading