From e9e5edf9672a2396ef54810060b7446c390c30c0 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 17:32:47 +0530 Subject: [PATCH 01/10] Add more REPL configuration options --- jupyterlite_sphinx/jupyterlite_sphinx.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 989a739..4d4d43f 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -1137,6 +1137,14 @@ def setup(app): "replite_new_tab_button_text", "Open in a REPL", rebuild="html" ) + # REPL configuration options + app.add_config_value("replite_auto_execute", True, rebuild="html") + app.add_config_value("replite_clear_cells_on_execute", False, rebuild="html") + app.add_config_value("replite_clear_code_content_on_execute", False, rebuild="html") + app.add_config_value("replite_hide_code_input", False, rebuild="html") + app.add_config_value("replite_prompt_cell_position", "bottom", rebuild="html") + app.add_config_value("replite_show_banner", True, rebuild="html") + # Initialize NotebookLite and JupyterLite directives app.add_node( NotebookLiteIframe, @@ -1186,7 +1194,6 @@ def setup(app): man=(skip, None), ) app.add_directive("replite", RepliteDirective) - app.add_config_value("replite_auto_execute", True, rebuild="html") # Initialize Voici directive and tabbed interface app.add_node( From d61e0fbc12369e631c15805e3886eebaaf1f510c Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 17:33:01 +0530 Subject: [PATCH 02/10] Bump to jupyterlite 0.6.0a9 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4473438..1b38054 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ "docutils", "jupyter_server", "jupyterlab_server", - "jupyterlite-core >=0.2,<0.6", + "jupyterlite-core==0.6.0a9", "jupytext", "nbformat", "sphinx>=4", From 0dde042ebd45a21fc2c0fe6d66ec834d952352e2 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 17:39:25 +0530 Subject: [PATCH 03/10] Add extra customisations to options_spec --- jupyterlite_sphinx/jupyterlite_sphinx.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 4d4d43f..59db243 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -405,6 +405,11 @@ class RepliteDirective(SphinxDirective): "height": directives.unchanged, "kernel": directives.unchanged, "execute": directives.unchanged, + "clear_cells_on_execute": directives.unchanged, + "clear_code_content_on_execute": directives.unchanged, + "hide_code_input": directives.unchanged, + "prompt_cell_position": directives.unchanged, + "show_banner": directives.unchanged, "toolbar": directives.unchanged, "theme": directives.unchanged, "prompt": directives.unchanged, From 6385f7be3fb3f82869a6baa43632a36ae3d2301b Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 18:00:53 +0530 Subject: [PATCH 04/10] Some black formatting fixes --- jupyterlite_sphinx/jupyterlite_sphinx.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 59db243..e5e3b41 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -85,7 +85,7 @@ def html(self): ) placeholder_id = uuid4() - container_style = f'width: {self["width"]}; height: {self["height"]};' + container_style = f"width: {self['width']}; height: {self['height']};" return f"""
Date: Wed, 7 May 2025 18:10:10 +0530 Subject: [PATCH 05/10] Handle Replite directive customisations logic --- jupyterlite_sphinx/jupyterlite_sphinx.py | 71 +++++++++++++++++++++--- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index e5e3b41..9c241f3 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -428,14 +428,69 @@ def run(self): search_params = search_params_parser(self.options.pop("search_params", False)) # We first check the global config, and then the per-directive - # option. It defaults to True for backwards compatibility. - execute = self.options.pop("execute", str(self.env.config.replite_auto_execute)) - - if execute not in ("True", "False"): - raise ValueError("The :execute: option must be either True or False") - - if execute == "False": - self.options["execute"] = "0" + # options, with reasonable defaults for backwards compatibility. + repl_config_mappings = { + "execute": "execute", + "clear_cells_on_execute": "clearCellsOnExecute", + "clear_code_content_on_execute": "clearCodeContentOnExecute", + "hide_code_input": "hideCodeInput", + "show_banner": "showBanner", + "prompt_cell_position": "promptCellPosition", + } + + for option in repl_config_mappings: + config_option = repl_config_mappings[option] + if option == "execute": + value = self.options.pop( + option, str(self.env.config.replite_auto_execute) + ) + elif option == "clear_cells_on_execute": + value = self.options.pop( + option, str(self.env.config.replite_clear_cells_on_execute) + ) + elif option == "clear_code_content_on_execute": + value = self.options.pop( + option, str(self.env.config.replite_clear_code_content_on_execute) + ) + elif option == "hide_code_input": + value = self.options.pop( + option, str(self.env.config.replite_hide_code_input) + ) + elif option == "show_banner": + value = self.options.pop( + option, str(self.env.config.replite_show_banner) + ) + elif option == "prompt_cell_position": + value = self.options.pop( + option, str(self.env.config.replite_prompt_cell_position) + ) + else: + err_msg = ( + f"Unknown option {option} for Replite directive. " + "Please check the documentation for valid options." + ) + raise ValueError(err_msg) + + # Convert to URL parameter format (0/1) for all options + # except for prompt_cell_position + if option != "prompt_cell_position": + if value.lower() == "true": + self.options[config_option] = "1" + elif value.lower() == "false": + self.options[config_option] = "0" + else: + err_msg = ( + f"The {option} option must be either True or False, not {value}" + ) + raise ValueError(err_msg) + else: + # For prompt_cell_position, we need to check if the value is valid + if value not in ["top", "bottom", "left", "right"]: + err_msg = ( + f"The {option} option must be one of: top, bottom, left, right" + ) + raise ValueError(err_msg) + self.options[config_option] = value content = self.content From 8d440eb92ef748998faf51c7f02fb69724ad42a9 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 18:10:24 +0530 Subject: [PATCH 06/10] Handle Replite tab customisations logic --- jupyterlite_sphinx/jupyterlite_sphinx.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/jupyterlite_sphinx/jupyterlite_sphinx.py b/jupyterlite_sphinx/jupyterlite_sphinx.py index 9c241f3..98a35b2 100644 --- a/jupyterlite_sphinx/jupyterlite_sphinx.py +++ b/jupyterlite_sphinx/jupyterlite_sphinx.py @@ -269,8 +269,27 @@ def __init__( code = "\n".join(code_lines) lite_options["code"] = code + # REPL URL parameters if "execute" in lite_options and lite_options["execute"] == "0": lite_options["execute"] = "0" + if ( + "clearCellsOnExecute" in lite_options + and lite_options["clearCellsOnExecute"] == "0" + ): + lite_options["clearCellsOnExecute"] = "0" + if ( + "clearCodeContentOnExecute" in lite_options + and lite_options["clearCodeContentOnExecute"] == "0" + ): + lite_options["clearCodeContentOnExecute"] = "0" + if "hideCodeInput" in lite_options and lite_options["hideCodeInput"] == "0": + lite_options["hideCodeInput"] = "0" + if "showBanner" in lite_options and lite_options["showBanner"] == "0": + lite_options["showBanner"] = "0" + if "promptCellPosition" in lite_options: + valid_positions = {"bottom", "top", "left", "right"} + if lite_options["promptCellPosition"] in valid_positions: + lite_options["promptCellPosition"] = lite_options["promptCellPosition"] app_path = self.lite_app if notebook is not None: From 6ca0e9045b59ada146e80ca61cbc87cb6d6593c7 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 18:19:25 +0530 Subject: [PATCH 07/10] Add docs on global REPL configuration --- docs/configuration.md | 51 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 3774fb9..10221c8 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -85,11 +85,50 @@ voici_new_tab_button_text = "My custom Voici button text" You can override this text on a per-directive basis by passing the `:new_tab_button_text:` option to the directive. Note that this is compatible only if `:new_tab:` is also provided. -## REPL code auto-execution with the `Replite` directive +## REPL configuration options -It is possible to control whether code snippets in REPL environments automatically executes when loaded. -For this, you may set `replite_auto_execute = False` globally in `conf.py` with (defaults to `True` if -not present), or override it on a per-directive basis with `:execute: True` or `:execute: False`. +We provide several configuration options for the Replite directive that control the behaviour and appearance of the REPL. These options can be set globally in `conf.py` and then overridden on a per-directive basis. + +### REPL code auto-execution + +```python +# enable or disable automatic code execution when the REPL loads +# (available in jupyterlite-core 0.5.0 and later) +replite_auto_execute = True # default is True +``` + +This setting controls whether code snippets in REPL environments automatically execute when loaded. Set to `False` to disable automatic execution. You can override this on a per-directive basis with `:execute: True` or `:execute: False`. + +### REPL interface customisations (JupyterLite 0.6.0 and later) + +The following options customise how the REPL interface behaves and is presented: + +```python +# clear previous cells when a new cell is executed +replite_clear_cells_on_execute = False # default is False + +# clear the code content in the prompt cell after execution +replite_clear_code_content_on_execute = False # default is False + +# hide input cells, showing only output +replite_hide_code_input = False # default is False + +# position of the prompt cell ('bottom', 'top', 'left', or 'right') +replite_prompt_cell_position = "bottom" # default is "bottom" + +# show or hide the kernel banner +replite_show_banner = True # default is True +``` + +These global settings can be overridden in individual directives using the corresponding options: + +- `:clear_cells_on_execute: True/False` +- `:clear_code_content_on_execute: True/False` +- `:hide_code_input: True/False` +- `:prompt_cell_position: bottom/top/left/right` +- `:show_banner: True/False` + +For more details and visual examples of each, see the [Replite directive documentation](directives/replite.md). ## Strip particular tagged cells from IPython Notebooks @@ -120,7 +159,7 @@ Sphinx-specific content. It can be used to remove either code cells or Markdown For example, you can use this feature to remove the `toctree` directive from the rendered notebook in the JupyterLite console: -```json +````json { "cells": [ { @@ -148,7 +187,7 @@ in the JupyterLite console: } ] } -``` +```` where the cell with the `toctree` directive will be removed from the rendered notebook in the JupyterLite console. From 21c7df50a0ca781ea4685527ce2c2d6f4f7504d6 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 18:20:02 +0530 Subject: [PATCH 08/10] Add docs on enhanced Replite configuration --- docs/directives/replite.md | 170 +++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/docs/directives/replite.md b/docs/directives/replite.md index 26f115f..33015ed 100644 --- a/docs/directives/replite.md +++ b/docs/directives/replite.md @@ -157,3 +157,173 @@ global value using an additional `:new_tab_button_text:` parameter: ``` ```` + +````{tip} + + ## Additional REPL interface options (JupyterLite 0.6.0+) + + With `jupyterlite-core` versions 0.6.0 and later, the REPL interface comes with several additional customisation options that provide more control over the execution environment and layout: + + ### Clearing cells on execute + + To automatically clear the previously executed cells when a new cell is executed, use the `:clear_cells_on_execute:` option: + + ```rst + .. replite:: + :kernel: xeus-python + :clear_cells_on_execute: True + + # When you execute this cell and then enter new code, + # this cell will disappear from the display + print("Hello, world!") + ``` + + ```{eval-rst} + .. replite:: + :kernel: xeus-python + :clear_cells_on_execute: True + + # When you execute this cell and then enter new code, + # this cell will disappear from the display + print("Hello, world!") + ``` + + ### Clearing code content on execute + + To automatically clear the code content in the prompt cell after execution, use the `:clear_code_on_execute:` option: + + ```rst + .. replite:: + :kernel: xeus-python + :clear_code_content_on_execute: True + + # After executing this cell, its content will be cleared, + # but the output will remain visible + print("The code will disappear but this output stays") + ``` + + ```{eval-rst} + .. replite:: + :kernel: xeus-python + :clear_code_content_on_execute: True + + # After executing this cell, its content will be cleared, + # but the output will remain visible + print("The code will disappear but this output stays") + ``` + + ### Hiding code input + + To hide the input cells after execution, showing only the output, use the `:hide_code_input:` option: + + ```rst + .. replite:: + :kernel: xeus-python + :hide_code_input: True + + # After executing this cell, the input will be hidden, + # but the output will remain visible + print("You'll see this output but not the code that generated it") + ``` + + ```{eval-rst} + .. replite:: + :kernel: xeus-python + :hide_code_input: True + + # After execution, this code will be hidden + # Only the output will be visible + print("You'll see this output but not the code that generated it") + ``` + + ### Changing prompt cell position + + By default, the prompt cell is positioned at the bottom of the REPL interface. You can change this using the `:prompt_cell_position:` option, which accepts `top`, `bottom`, `left`, or `right`: + + ```rst + .. replite:: + :kernel: xeus-python + :prompt_cell_position: top + + # The prompt will appear at the top of the REPL + print("Input above, output below") + ``` + + ```{eval-rst} + .. replite:: + :kernel: xeus-python + :prompt_cell_position: top + + # The prompt will appear at the top of the REPL + print("Input above, output below") + ``` + + ### Showing or hiding the kernel banner + + By default, the REPL shows the kernel banner with version information. To hide this banner, use the `:show_banner:` option: + + ```rst + .. replite:: + :kernel: xeus-python + :show_banner: False + + # The kernel banner won't be displayed + print("No banner here") + ``` + + ```{eval-rst} + .. replite:: + :kernel: xeus-python + :show_banner: False + + # The kernel banner won't be displayed + print("No banner here") + ``` + + ### Combining options + All of these options can be combined to create a customised REPL experience, + for example: + + ```rst + .. replite:: + :kernel: xeus-python + :prompt_cell_position: left + :hide_code_input: True + :show_banner: False + :height: 400px + + # This will create a clean output-only display + # with the input cell on the left + import matplotlib.pyplot as plt + import numpy as np + + x = np.linspace(0, 2 * np.pi, 200) + y = np.sin(x) + + fig, ax = plt.subplots() + ax.plot(x, y) + plt.show() + ``` + + ```{eval-rst} + .. replite:: + :kernel: xeus-python + :prompt_cell_position: left + :hide_code_input: True + :show_banner: False + :height: 400px + + # This will create a clean output-only display + # with the input cell on the left + import matplotlib.pyplot as plt + import numpy as np + + x = np.linspace(0, 2 * np.pi, 200) + y = np.sin(x) + + fig, ax = plt.subplots() + ax.plot(x, y) + plt.show() + ``` + +```` From d9dc2c99b8206d8412f5002d884b22e43ac14b06 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 18:30:14 +0530 Subject: [PATCH 09/10] Install JupyterLite alpha in RTD deployment --- dev-environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-environment.yml b/dev-environment.yml index 2547bde..5c001ca 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -5,7 +5,6 @@ dependencies: - pip - jupyter_server - jupyterlab_server - - jupyterlite-core >=0.3,<0.6 - jupytext - pydata-sphinx-theme - micromamba=2.0.5 @@ -16,4 +15,5 @@ dependencies: - voici - pip: - . + - jupyterlite-core ==0.6.0a9 - jupyterlite-xeus >=2.1.2 From e491d48193d2adb84c8fdee0afabcb81aa7655a6 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Wed, 7 May 2025 18:32:59 +0530 Subject: [PATCH 10/10] Temporarily relax jupyterlite-xeus as well --- dev-environment.yml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-environment.yml b/dev-environment.yml index 5c001ca..582813c 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -16,4 +16,4 @@ dependencies: - pip: - . - jupyterlite-core ==0.6.0a9 - - jupyterlite-xeus >=2.1.2 + # - jupyterlite-xeus >=2.1.2 diff --git a/pyproject.toml b/pyproject.toml index 1b38054..10a8f05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dev = [ docs = [ "myst_parser", "pydata-sphinx-theme", - "jupyterlite-xeus>=0.1.8,<4", + # "jupyterlite-xeus>=0.1.8,<4", ] [tool.hatch.version]