@@ -236,6 +236,66 @@ class NotebookLiteTab(BaseNotebookTab):
236
236
notebooks_path = "../notebooks/"
237
237
238
238
239
+ # We do not inherit from _InTab here because Replite
240
+ # has a different URL structure and we need to ensure
241
+ # that the code is serialised to be passed to the URL.
242
+ class RepliteTab (Element ):
243
+ """Appended to the doctree by the RepliteDirective directive
244
+
245
+ Renders a button that opens a REPL with JupyterLite in a new tab.
246
+ """
247
+
248
+ lite_app = "repl/"
249
+ notebooks_path = ""
250
+
251
+ def __init__ (
252
+ self ,
253
+ rawsource = "" ,
254
+ * children ,
255
+ prefix = JUPYTERLITE_DIR ,
256
+ content = [],
257
+ notebook = None ,
258
+ lite_options = {},
259
+ button_text = None ,
260
+ ** attributes ,
261
+ ):
262
+ # For a new-tabbed variant, we need to ensure we process the content
263
+ # into properly encoded code for passing it to the URL.
264
+ if content :
265
+ code_lines : list [str ] = [
266
+ "" if not line .strip () else line for line in content
267
+ ]
268
+ code = "\n " .join (code_lines )
269
+ lite_options ["code" ] = code
270
+
271
+ app_path = self .lite_app
272
+ if notebook is not None :
273
+ lite_options ["path" ] = notebook
274
+ app_path = f"{ self .lite_app } { self .notebooks_path } "
275
+
276
+ options = "&" .join (
277
+ [f"{ key } ={ quote (value )} " for key , value in lite_options .items ()]
278
+ )
279
+
280
+ self .lab_src = (
281
+ f'{ prefix } /{ app_path } { f"index.html?{ options } " if options else "" } '
282
+ )
283
+
284
+ self .button_text = button_text
285
+
286
+ super ().__init__ (
287
+ rawsource ,
288
+ ** attributes ,
289
+ )
290
+
291
+ def html (self ):
292
+ return (
293
+ '<button class="try_examples_button" '
294
+ f"onclick=\" window.open('{ self .lab_src } ')\" >"
295
+ f"{ self .button_text } </button>"
296
+ )
297
+
298
+
239
299
class NotebookLiteIframe (_LiteIframe ):
240
300
"""Appended to the doctree by the NotebookliteDirective directive
241
301
@@ -345,6 +405,8 @@ class RepliteDirective(SphinxDirective):
345
405
"prompt" : directives .unchanged ,
346
406
"prompt_color" : directives .unchanged ,
347
407
"search_params" : directives .unchanged ,
408
+ "new_tab" : directives .unchanged ,
409
+ "new_tab_button_text" : directives .unchanged ,
348
410
}
349
411
350
412
def run (self ):
@@ -356,19 +418,45 @@ def run(self):
356
418
357
419
search_params = search_params_parser (self .options .pop ("search_params" , False ))
358
420
421
+ new_tab = self .options .pop ("new_tab" , False )
422
+
423
+ content = self .content
424
+
425
+ button_text = None
426
+
359
427
prefix = os .path .relpath (
360
428
os .path .join (self .env .app .srcdir , JUPYTERLITE_DIR ),
361
429
os .path .dirname (self .get_source_info ()[0 ]),
362
430
)
363
431
432
+ if new_tab :
433
+ directive_button_text = self .options .pop ("new_tab_button_text" , None )
434
+ if directive_button_text is not None :
435
+ button_text = directive_button_text
436
+ else :
437
+ button_text = self .env .config .replite_new_tab_button_text
438
+ return [
439
+ RepliteTab (
440
+ prefix = prefix ,
441
+ width = width ,
442
+ height = height ,
443
+ prompt = prompt ,
444
+ prompt_color = prompt_color ,
445
+ content = content ,
446
+ search_params = search_params ,
447
+ lite_options = self .options ,
448
+ button_text = button_text ,
449
+ )
450
+ ]
451
+
364
452
return [
365
453
RepliteIframe (
366
454
prefix = prefix ,
367
455
width = width ,
368
456
height = height ,
369
457
prompt = prompt ,
370
458
prompt_color = prompt_color ,
371
- content = self . content ,
459
+ content = content ,
372
460
search_params = search_params ,
373
461
lite_options = self .options ,
374
462
)
@@ -387,7 +475,7 @@ class _LiteDirective(SphinxDirective):
387
475
"prompt_color" : directives .unchanged ,
388
476
"search_params" : directives .unchanged ,
389
477
"new_tab" : directives .unchanged ,
390
- "button_text " : directives .unchanged ,
478
+ "new_tab_button_text " : directives .unchanged ,
391
479
}
392
480
393
481
def run (self ):
@@ -451,24 +539,19 @@ def run(self):
451
539
notebook_name = None
452
540
453
541
if new_tab :
454
- directive_button_text = self .options .pop ("button_text " , None )
542
+ directive_button_text = self .options .pop ("new_tab_button_text " , None )
455
543
if directive_button_text is not None :
456
544
button_text = directive_button_text
457
545
else :
458
546
# If none, we use the appropriate global config based on
459
547
# the type of directive passed.
460
548
if isinstance (self , JupyterLiteDirective ):
461
- button_text = self .env .config .jupyterlite_button_text
549
+ button_text = self .env .config .jupyterlite_new_tab_button_text
462
550
elif isinstance (self , NotebookLiteDirective ):
463
- button_text = self .env .config .notebooklite_button_text
551
+ button_text = self .env .config .notebooklite_new_tab_button_text
464
552
elif isinstance (self , VoiciDirective ):
465
- button_text = self .env .config .voici_button_text
466
- elif "button_text" in self .options :
467
- raise ValueError (
468
- "'button_text' is only valid if 'new_tab' is True. To modify the prompt text, use 'prompt' and 'prompt_color'."
469
- )
553
+ button_text = self .env .config .voici_new_tab_button_text
470
554
471
- if new_tab :
472
555
return [
473
556
self .newtab_cls (
474
557
prefix = prefix ,
@@ -511,9 +594,9 @@ class BaseJupyterViewDirective(_LiteDirective):
511
594
"prompt_color" : directives .unchanged ,
512
595
"search_params" : directives .unchanged ,
513
596
"new_tab" : directives .unchanged ,
514
- # "button_text " below is valid only if "new_tab" is True, otherwise
597
+ # "new_tab_button_text " below is useful only if "new_tab" is True, otherwise
515
598
# we have "prompt" and "prompt_color" as options already.
516
- "button_text " : directives .unchanged ,
599
+ "new_tab_button_text " : directives .unchanged ,
517
600
}
518
601
519
602
@@ -927,15 +1010,18 @@ def setup(app):
927
1010
rebuild = "html" ,
928
1011
)
929
1012
930
- # Allow customising the button text for each directive (only when "new_tab" is True,
931
- # error otherwise)
1013
+ # Allow customising the button text for each directive (this is useful
1014
+ # only when "new_tab" is set to True)
1015
+ app .add_config_value (
1016
+ "jupyterlite_new_tab_button_text" , "Open as a notebook" , rebuild = "html"
1017
+ )
932
1018
app .add_config_value (
933
- "jupyterlite_button_text " , "Open as a notebook" , rebuild = "html"
1019
+ "notebooklite_new_tab_button_text " , "Open as a notebook" , rebuild = "html"
934
1020
)
1021
+ app .add_config_value ("voici_new_tab_button_text" , "Open with Voici" , rebuild = "html" )
935
1022
app .add_config_value (
936
- "notebooklite_button_text " , "Open as a notebook " , rebuild = "html"
1023
+ "replite_new_tab_button_text " , "Open in a REPL " , rebuild = "html"
937
1024
)
938
- app .add_config_value ("voici_button_text" , "Open with Voici" , rebuild = "html" )
939
1025
940
1026
# Initialize NotebookLite and JupyterLite directives
941
1027
app .add_node (
@@ -968,7 +1054,7 @@ def setup(app):
968
1054
)
969
1055
app .add_directive ("jupyterlite" , JupyterLiteDirective )
970
1056
971
- # Initialize Replite directive
1057
+ # Initialize Replite directive and tab
972
1058
app .add_node (
973
1059
RepliteIframe ,
974
1060
html = (visit_element_html , None ),
@@ -977,6 +1063,14 @@ def setup(app):
977
1063
text = (skip , None ),
978
1064
man = (skip , None ),
979
1065
)
1066
+ app .add_node (
1067
+ RepliteTab ,
1068
+ html = (visit_element_html , None ),
1069
+ latex = (skip , None ),
1070
+ textinfo = (skip , None ),
1071
+ text = (skip , None ),
1072
+ man = (skip , None ),
1073
+ )
980
1074
app .add_directive ("replite" , RepliteDirective )
981
1075
982
1076
# Initialize Voici directive and tabbed interface
0 commit comments