2323
2424CONTENT_DIR = "_contents"
2525JUPYTERLITE_DIR = "lite"
26+ # Using a global variable, is there a better way?
27+ APPS = []
2628
2729
2830# Used for nodes that do not need to be rendered
@@ -36,52 +38,29 @@ def visit_element_html(self, node):
3638 raise SkipNode
3739
3840
39- class _LiteIframe (Element ):
41+ class _PromptedIframe (Element ):
4042 def __init__ (
4143 self ,
4244 rawsource = "" ,
4345 * children ,
44- prefix = JUPYTERLITE_DIR ,
46+ iframe_src = "" ,
4547 width = "100%" ,
4648 height = "100%" ,
4749 prompt = False ,
4850 prompt_color = None ,
49- content = [],
50- notebook = None ,
51- lite_options = {},
5251 ** attributes ,
5352 ):
5453 super ().__init__ (
5554 "" ,
56- prefix = prefix ,
55+ iframe_src = iframe_src ,
5756 width = width ,
5857 height = height ,
5958 prompt = prompt ,
6059 prompt_color = prompt_color ,
61- content = content ,
62- notebook = notebook ,
63- lite_options = lite_options ,
6460 )
6561
6662 def html (self ):
67- lite_options = self ["lite_options" ]
68-
69- if self ["content" ]:
70- code_lines = ["" if not line .strip () else line for line in self ["content" ]]
71- code = "\n " .join (code_lines )
72-
73- lite_options ["code" ] = code
74-
75- app_path = self .lite_app
76- if self ["notebook" ] is not None :
77- lite_options ["path" ] = self ["notebook" ]
78- app_path = f"{ self .lite_app } { self .notebooks_path } "
79-
80- options = "&" .join (
81- [f"{ key } ={ quote (value )} " for key , value in lite_options .items ()]
82- )
83-
84- iframe_src = f'{ self ["prefix" ]} /{ app_path } { f"?{ options } " if options else "" } '
63+ iframe_src = self ["iframe_src" ]
8564
8665 if self ["prompt" ]:
8766 prompt = (
@@ -108,6 +87,37 @@ def html(self):
10887 )
10988
11089
90+ class _LiteIframe (_PromptedIframe ):
91+ def __init__ (
92+ self ,
93+ rawsource = "" ,
94+ * children ,
95+ prefix = JUPYTERLITE_DIR ,
96+ content = [],
97+ notebook = None ,
98+ lite_options = {},
99+ ** attributes ,
100+ ):
101+ if content :
102+ code_lines = ["" if not line .strip () else line for line in content ]
103+ code = "\n " .join (code_lines )
104+
105+ lite_options ["code" ] = code
106+
107+ app_path = self .lite_app
108+ if notebook is not None :
109+ lite_options ["path" ] = notebook
110+ app_path = f"{ self .lite_app } { self .notebooks_path } "
111+
112+ options = "&" .join (
113+ [f"{ key } ={ quote (value )} " for key , value in lite_options .items ()]
114+ )
115+
116+ iframe_src = f'{ prefix } /{ app_path } { f"?{ options } " if options else "" } '
117+
118+ super ().__init__ (rawsource , * children , iframe_src = iframe_src , ** attributes )
119+
120+
111121class RepliteIframe (_LiteIframe ):
112122 """Appended to the doctree by the RepliteDirective directive
113123
@@ -138,6 +148,35 @@ class RetroLiteIframe(_LiteIframe):
138148 notebooks_path = "notebooks/"
139149
140150
151+ class VoiciIframe (_PromptedIframe ):
152+ """Appended to the doctree by the VoiciDirective directive
153+
154+ Renders an iframe that shows a Notebook with Voici.
155+ """
156+
157+ def __init__ (
158+ self ,
159+ rawsource = "" ,
160+ * children ,
161+ prefix = JUPYTERLITE_DIR ,
162+ notebook = None ,
163+ lite_options = {},
164+ ** attributes ,
165+ ):
166+ if notebook is not None :
167+ app_path = f"voici/render/{ notebook .replace ('.ipynb' , '.html' )} "
168+ else :
169+ app_path = "voici/tree"
170+
171+ options = "&" .join (
172+ [f"{ key } ={ quote (value )} " for key , value in lite_options .items ()]
173+ )
174+
175+ iframe_src = f'{ prefix } /{ app_path } { f"?{ options } " if options else "" } '
176+
177+ super ().__init__ (rawsource , * children , iframe_src = iframe_src , ** attributes )
178+
179+
141180class RepliteDirective (SphinxDirective ):
142181 """The ``.. replite::`` directive.
143182
@@ -157,6 +196,9 @@ class RepliteDirective(SphinxDirective):
157196 }
158197
159198 def run (self ):
199+ if not "repl" in APPS :
200+ APPS .append ("repl" )
201+
160202 width = self .options .pop ("width" , "100%" )
161203 height = self .options .pop ("height" , "100%" )
162204
@@ -247,6 +289,12 @@ class JupyterLiteDirective(_LiteDirective):
247289
248290 iframe_cls = JupyterLiteIframe
249291
292+ def run (self ):
293+ if not "lab" in APPS :
294+ APPS .append ("lab" )
295+
296+ return super ().run ()
297+
250298
251299class RetroLiteDirective (_LiteDirective ):
252300 """The ``.. retrolite::`` directive.
@@ -256,6 +304,34 @@ class RetroLiteDirective(_LiteDirective):
256304
257305 iframe_cls = RetroLiteIframe
258306
307+ def run (self ):
308+ if not "retro" in APPS :
309+ APPS .append ("retro" )
310+
311+ return super ().run ()
312+
313+
314+ class VoiciDirective (_LiteDirective ):
315+ """The ``.. voici::`` directive.
316+
317+ Renders a Notebook with Voici in the docs.
318+ """
319+
320+ iframe_cls = VoiciIframe
321+
322+ def run (self ):
323+ try :
324+ import voici
325+ except ImportError :
326+ raise RuntimeError (
327+ "Voici must be installed if you want to make use of the voici directive: pip install voici"
328+ )
329+
330+ if not "voici" in APPS :
331+ APPS .append ("voici" )
332+
333+ return super ().run ()
334+
259335
260336class RetroLiteParser (RSTParser ):
261337 """Sphinx source parser for Jupyter notebooks.
@@ -317,12 +393,17 @@ def jupyterlite_build(app: Sphinx, error):
317393 for content in jupyterlite_contents :
318394 contents .extend (["--contents" , content ])
319395
396+ apps = []
397+ for jlite_app in APPS :
398+ apps .extend (["--apps" , jlite_app ])
399+
320400 command = [
321401 "jupyter" ,
322402 "lite" ,
323403 "build" ,
324404 "--debug" ,
325405 * config ,
406+ * apps ,
326407 * contents ,
327408 "--contents" ,
328409 os .path .join (app .srcdir , CONTENT_DIR ),
@@ -389,6 +470,17 @@ def setup(app):
389470 )
390471 app .add_directive ("replite" , RepliteDirective )
391472
473+ # Initialize Voici directive
474+ app .add_node (
475+ VoiciIframe ,
476+ html = (visit_element_html , None ),
477+ latex = (skip , None ),
478+ textinfo = (skip , None ),
479+ text = (skip , None ),
480+ man = (skip , None ),
481+ )
482+ app .add_directive ("voici" , VoiciDirective )
483+
392484 # CSS and JS assets
393485 copy_asset (str (HERE / "jupyterlite_sphinx.css" ), str (Path (app .outdir ) / "_static" ))
394486 copy_asset (str (HERE / "jupyterlite_sphinx.js" ), str (Path (app .outdir ) / "_static" ))
0 commit comments