Skip to content

Commit 4b207c2

Browse files
Establish VoiciBase class, add tabbed VoiciDirective
1 parent 7392b44 commit 4b207c2

File tree

1 file changed

+70
-11
lines changed

1 file changed

+70
-11
lines changed

jupyterlite_sphinx/jupyterlite_sphinx.py

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,21 @@ class NotebookLiteIframe(_LiteIframe):
242242
notebooks_path = "../notebooks/"
243243

244244

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+
245260
class VoiciIframe(_PromptedIframe):
246261
"""Appended to the doctree by the VoiciDirective directive
247262
@@ -257,20 +272,55 @@ def __init__(
257272
lite_options={},
258273
**attributes,
259274
):
260-
if notebook is not None:
261-
app_path = f"voici/render/{notebook.replace('.ipynb', '.html')}"
262-
else:
263-
app_path = "voici/tree"
264-
275+
app_path = VoiciBase.get_full_path(notebook)
265276
options = "&".join(
266277
[f"{key}={quote(value)}" for key, value in lite_options.items()]
267278
)
268279

280+
# If a notebook is provided, open it in the render view. Else, we default to the tree view.
269281
iframe_src = f'{prefix}/{app_path}{f"index.html?{options}" if options else ""}'
270282

271283
super().__init__(rawsource, *children, iframe_src=iframe_src, **attributes)
272284

273285

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+
274324
class RepliteDirective(SphinxDirective):
275325
"""The ``.. replite::`` directive.
276326
@@ -418,8 +468,8 @@ def run(self):
418468
]
419469

420470

421-
class BaseNotebookDirective(_LiteDirective):
422-
"""Base class for notebook directives."""
471+
class BaseJupyterViewDirective(_LiteDirective):
472+
"""Base class for jupyterlite-sphinx directives."""
423473

424474
iframe_cls = None # to be defined by subclasses
425475
newtab_cls = None # to be defined by subclasses
@@ -435,7 +485,7 @@ class BaseNotebookDirective(_LiteDirective):
435485
}
436486

437487

438-
class JupyterLiteDirective(BaseNotebookDirective):
488+
class JupyterLiteDirective(BaseJupyterViewDirective):
439489
"""The ``.. jupyterlite::`` directive.
440490
441491
Renders a Notebook with JupyterLite in the docs.
@@ -445,7 +495,7 @@ class JupyterLiteDirective(BaseNotebookDirective):
445495
newtab_cls = JupyterLiteTab
446496

447497

448-
class NotebookLiteDirective(BaseNotebookDirective):
498+
class NotebookLiteDirective(BaseJupyterViewDirective):
449499
"""The ``.. notebooklite::`` directive.
450500
451501
Renders a Notebook with NotebookLite in the docs.
@@ -455,13 +505,14 @@ class NotebookLiteDirective(BaseNotebookDirective):
455505
newtab_cls = NotebookLiteTab
456506

457507

458-
class VoiciDirective(_LiteDirective):
508+
class VoiciDirective(BaseJupyterViewDirective):
459509
"""The ``.. voici::`` directive.
460510
461511
Renders a Notebook with Voici in the docs.
462512
"""
463513

464514
iframe_cls = VoiciIframe
515+
newtab_cls = VoiciTab
465516

466517
def run(self):
467518
if voici is None:
@@ -868,7 +919,7 @@ def setup(app):
868919
)
869920
app.add_directive("replite", RepliteDirective)
870921

871-
# Initialize Voici directive
922+
# Initialize Voici directive and tabbed interface
872923
app.add_node(
873924
VoiciIframe,
874925
html=(visit_element_html, None),
@@ -877,6 +928,14 @@ def setup(app):
877928
text=(skip, None),
878929
man=(skip, None),
879930
)
931+
app.add_node(
932+
VoiciTab,
933+
html=(visit_element_html, None),
934+
latex=(skip, None),
935+
textinfo=(skip, None),
936+
text=(skip, None),
937+
man=(skip, None),
938+
)
880939
app.add_directive("voici", VoiciDirective)
881940

882941
# Initialize TryExamples directive

0 commit comments

Comments
 (0)