@@ -204,7 +204,15 @@ class JupyterLiteIframe(_LiteIframe):
204
204
notebooks_path = ""
205
205
206
206
207
- class JupyterLiteTab (_InTab ):
207
+ class BaseNotebookTab (_InTab ):
208
+ """Base class for notebook tab implementations. We subclass this
209
+ to create more specific configurations around how tabs are rendered."""
210
+
211
+ lite_app = None
212
+ notebooks_path = None
213
+
214
+
215
+ class JupyterLiteTab (BaseNotebookTab ):
208
216
"""Appended to the doctree by the JupyterliteDirective directive
209
217
210
218
Renders a button that opens a Notebook with JupyterLite in a new tab.
@@ -214,6 +222,16 @@ class JupyterLiteTab(_InTab):
214
222
notebooks_path = ""
215
223
216
224
225
+ class NotebookLiteTab (BaseNotebookTab ):
226
+ """Appended to the doctree by the NotebookliteDirective directive
227
+
228
+ Renders a button that opens a Notebook with NotebookLite in a new tab.
229
+ """
230
+
231
+ lite_app = "tree/"
232
+ notebooks_path = "../notebooks/"
233
+
234
+
217
235
class NotebookLiteIframe (_LiteIframe ):
218
236
"""Appended to the doctree by the NotebookliteDirective directive
219
237
@@ -224,6 +242,21 @@ class NotebookLiteIframe(_LiteIframe):
224
242
notebooks_path = "../notebooks/"
225
243
226
244
245
+ class VoiciBase :
246
+ """Base class with common Voici application paths and URL structure"""
247
+
248
+ lite_app = "voici/"
249
+
250
+ @classmethod
251
+ def get_full_path (cls , notebook = None ):
252
+ """Get the complete Voici path based on whether a notebook is provided."""
253
+ if notebook is not None :
254
+ # For notebooks, use render path with html extension
255
+ return f"{ cls .lite_app } render/{ notebook .replace ('.ipynb' , '.html' )} "
256
+ # Default to tree view
257
+ return f"{ cls .lite_app } tree"
258
+
259
+
227
260
class VoiciIframe (_PromptedIframe ):
228
261
"""Appended to the doctree by the VoiciDirective directive
229
262
@@ -239,20 +272,55 @@ def __init__(
239
272
lite_options = {},
240
273
** attributes ,
241
274
):
242
- if notebook is not None :
243
- app_path = f"voici/render/{ notebook .replace ('.ipynb' , '.html' )} "
244
- else :
245
- app_path = "voici/tree"
246
-
275
+ app_path = VoiciBase .get_full_path (notebook )
247
276
options = "&" .join (
248
277
[f"{ key } ={ quote (value )} " for key , value in lite_options .items ()]
249
278
)
250
279
280
+ # If a notebook is provided, open it in the render view. Else, we default to the tree view.
251
281
iframe_src = f'{ prefix } /{ app_path } { f"index.html?{ options } " if options else "" } '
252
282
253
283
super ().__init__ (rawsource , * children , iframe_src = iframe_src , ** attributes )
254
284
255
285
286
+ # We do not inherit from BaseNotebookTab here because
287
+ # Voici has a different URL structure.
288
+ class VoiciTab (Element ):
289
+ """Tabbed implementation for the Voici interface"""
290
+
291
+ def __init__ (
292
+ self ,
293
+ rawsource = "" ,
294
+ * children ,
295
+ prefix = JUPYTERLITE_DIR ,
296
+ notebook = None ,
297
+ lite_options = {},
298
+ ** attributes ,
299
+ ):
300
+
301
+ self .lab_src = f"{ prefix } /"
302
+
303
+ app_path = VoiciBase .get_full_path (notebook )
304
+ options = "&" .join (
305
+ [f"{ key } ={ quote (value )} " for key , value in lite_options .items ()]
306
+ )
307
+
308
+ # If a notebook is provided, open it in a new tab. Else, we default to the tree view.
309
+ self .lab_src = f'{ prefix } /{ app_path } { f"?{ options } " if options else "" } '
310
+
311
+ super ().__init__ (
312
+ rawsource ,
313
+ ** attributes ,
314
+ )
315
+
316
+ def html (self ):
317
+ return (
318
+ '<button class="try_examples_button" '
319
+ f"onclick=\" window.open('{ self .lab_src } ')\" >"
320
+ "Open with Voici</button>"
321
+ )
322
+
323
+
256
324
class RepliteDirective (SphinxDirective ):
257
325
"""The ``.. replite::`` directive.
258
326
@@ -400,7 +468,24 @@ def run(self):
400
468
]
401
469
402
470
403
- class JupyterLiteDirective (_LiteDirective ):
471
+ class BaseJupyterViewDirective (_LiteDirective ):
472
+ """Base class for jupyterlite-sphinx directives."""
473
+
474
+ iframe_cls = None # to be defined by subclasses
475
+ newtab_cls = None # to be defined by subclasses
476
+
477
+ option_spec = {
478
+ "width" : directives .unchanged ,
479
+ "height" : directives .unchanged ,
480
+ "theme" : directives .unchanged ,
481
+ "prompt" : directives .unchanged ,
482
+ "prompt_color" : directives .unchanged ,
483
+ "search_params" : directives .unchanged ,
484
+ "new_tab" : directives .unchanged ,
485
+ }
486
+
487
+
488
+ class JupyterLiteDirective (BaseJupyterViewDirective ):
404
489
"""The ``.. jupyterlite::`` directive.
405
490
406
491
Renders a Notebook with JupyterLite in the docs.
@@ -410,22 +495,24 @@ class JupyterLiteDirective(_LiteDirective):
410
495
newtab_cls = JupyterLiteTab
411
496
412
497
413
- class NotebookLiteDirective (_LiteDirective ):
498
+ class NotebookLiteDirective (BaseJupyterViewDirective ):
414
499
"""The ``.. notebooklite::`` directive.
415
500
416
501
Renders a Notebook with NotebookLite in the docs.
417
502
"""
418
503
419
504
iframe_cls = NotebookLiteIframe
505
+ newtab_cls = NotebookLiteTab
420
506
421
507
422
- class VoiciDirective (_LiteDirective ):
508
+ class VoiciDirective (BaseJupyterViewDirective ):
423
509
"""The ``.. voici::`` directive.
424
510
425
511
Renders a Notebook with Voici in the docs.
426
512
"""
427
513
428
514
iframe_cls = VoiciIframe
515
+ newtab_cls = VoiciTab
429
516
430
517
def run (self ):
431
518
if voici is None :
@@ -810,30 +897,39 @@ def setup(app):
810
897
text = (skip , None ),
811
898
man = (skip , None ),
812
899
)
900
+ for node_class in [NotebookLiteTab , JupyterLiteTab ]:
901
+ app .add_node (
902
+ node_class ,
903
+ html = (visit_element_html , None ),
904
+ latex = (skip , None ),
905
+ textinfo = (skip , None ),
906
+ text = (skip , None ),
907
+ man = (skip , None ),
908
+ )
909
+ app .add_directive ("jupyterlite" , JupyterLiteDirective )
910
+
911
+ # Initialize Replite directive
813
912
app .add_node (
814
- JupyterLiteTab ,
913
+ RepliteIframe ,
815
914
html = (visit_element_html , None ),
816
915
latex = (skip , None ),
817
916
textinfo = (skip , None ),
818
917
text = (skip , None ),
819
918
man = (skip , None ),
820
919
)
821
- app .add_directive ("jupyterlite " , JupyterLiteDirective )
920
+ app .add_directive ("replite " , RepliteDirective )
822
921
823
- # Initialize Replite directive
922
+ # Initialize Voici directive and tabbed interface
824
923
app .add_node (
825
- RepliteIframe ,
924
+ VoiciIframe ,
826
925
html = (visit_element_html , None ),
827
926
latex = (skip , None ),
828
927
textinfo = (skip , None ),
829
928
text = (skip , None ),
830
929
man = (skip , None ),
831
930
)
832
- app .add_directive ("replite" , RepliteDirective )
833
-
834
- # Initialize Voici directive
835
931
app .add_node (
836
- VoiciIframe ,
932
+ VoiciTab ,
837
933
html = (visit_element_html , None ),
838
934
latex = (skip , None ),
839
935
textinfo = (skip , None ),
0 commit comments