Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bc2e945
Add extension-examples as submodule
MUFFANUJ Mar 6, 2026
4025a5d
Integrate extension-examples submodule and add examples sidebar
MUFFANUJ Mar 6, 2026
5e2e4c5
Added a cool new icon
MUFFANUJ Mar 6, 2026
57519b7
Merge branch 'main' into integrateExtExamples
MUFFANUJ Mar 6, 2026
93080b5
Merge branch 'main' into integrateExtExamples
MUFFANUJ Mar 6, 2026
9dc5c8f
add suggestion
MUFFANUJ Mar 6, 2026
34cb2f2
run prettier
MUFFANUJ Mar 6, 2026
470999d
update test
MUFFANUJ Mar 6, 2026
21921d2
Merge branch 'main' into integrateExtExamples
MUFFANUJ Mar 6, 2026
ca04e92
Use submodule-only setup for extension-examples in docs build
MUFFANUJ Mar 7, 2026
6513b5d
remove unused icon
MUFFANUJ Mar 7, 2026
83083c7
Merge branch 'main' into integrateExtExamples
MUFFANUJ Mar 7, 2026
c0b4a68
Fix extension example descriptions on Lite content API
MUFFANUJ Mar 7, 2026
de95536
try fix
MUFFANUJ Mar 7, 2026
0745ec1
Improve path compatibility for example discovery
MUFFANUJ Mar 7, 2026
4874418
Merge branch 'main' into integrateExtExamples
MUFFANUJ Mar 7, 2026
729ea30
add suggestions
MUFFANUJ Mar 7, 2026
e97157a
remove default expansion
MUFFANUJ Mar 7, 2026
717d7c8
use collapse
MUFFANUJ Mar 7, 2026
c93c743
update tests
MUFFANUJ Mar 7, 2026
5cdd36a
Merge branch 'main' into integrateExtExamples
MUFFANUJ Mar 9, 2026
49b2d44
fix example import and schema resolution + add submodule to ignore li…
MUFFANUJ Mar 13, 2026
910f1c0
fix example import and schema resolution + add submodule to ignore li…
MUFFANUJ Mar 13, 2026
2b3e804
add new devdependencies
MUFFANUJ Mar 13, 2026
f130ae7
revert collapse on startup
MUFFANUJ Mar 13, 2026
207e35d
reset irrelevant test file changes
MUFFANUJ Mar 13, 2026
d62803e
support multi-schema example loading
MUFFANUJ Mar 13, 2026
3ffdf16
skip unsupported example plugins
MUFFANUJ Mar 13, 2026
36212f4
updated yarn.lock
MUFFANUJ Mar 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true

- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
Expand All @@ -26,6 +28,11 @@ jobs:
jlpm
jlpm run lint:check

- name: Validate extension examples compile
run: |
set -eux
jlpm run check:extension-examples

- name: Build the extension
run: |
set -eux
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ coverage.xml

# Sphinx documentation
docs/_build/
docs/content/extension-examples/

# PyBuilder
target/
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "extension-examples"]
path = extension-examples
url = https://github.com/jupyterlab/extension-examples.git
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ node_modules
**/package.json
jupyterlab_plugin_playground
src/modules.ts
docs/content
docs/content
extension-examples
4 changes: 4 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ build:
conda:
environment: docs/environment.yml

submodules:
include: all
recursive: true

sphinx:
configuration: docs/conf.py
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,17 @@ pip install jupyterlab-plugin-playground

This extension provides a new command, `Load Current File As Extension`, available in the text editor.

It also adds a right sidebar panel listing token string IDs you can use in plugin `requires` and `optional` arrays, with search, copy, and import actions.
It also adds a single right sidebar panel with two collapsible sections:

- **Service Tokens**: token string IDs you can use in plugin `requires` and `optional` arrays, with search, copy, and import actions.
- **Extension Examples**: discovered examples from a local checkout of [`jupyterlab/extension-examples`](https://github.com/jupyterlab/extension-examples), so you can open them directly from the panel.

If examples are missing:

- For source checkouts: run `git submodule update --init --recursive`.
- For PyPI installs: clone `https://github.com/jupyterlab/extension-examples` into an `extension-examples/` folder in your working directory.

When reloading a plugin with the same `id`, the playground attempts to deactivate the previously loaded plugin first. Defining `deactivate()` in examples is recommended for clean reruns.

As an example, open the text editor by creating a new text file and paste this small JupyterLab plugin into it. This plugin will create a simple command `My Super Cool Toggle` in the command palette that can be toggled on and off.

Expand Down
23 changes: 21 additions & 2 deletions binder/postBuild
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,27 @@ SETTINGS = Path(sys.prefix) / "share/jupyter/lab/settings"
SETTINGS.mkdir(parents=True, exist_ok=True)
shutil.copy2("binder/overrides.json", SETTINGS / "overrides.json")

# download examples
_("git", "clone", "https://github.com/jupyterlab/extension-examples.git")
# ensure examples are available (from submodule checkout or fallback clone)
EXAMPLES = ROOT / "extension-examples"
if (EXAMPLES / "README.md").exists():
print("Using extension examples from repository checkout/submodule.")
else:
if EXAMPLES.exists() and (ROOT / ".git").exists():
print("Attempting to initialize extension examples submodule.")
subprocess.call(
[
"git",
"submodule",
"update",
"--init",
"--recursive",
"extension-examples",
]
)
if EXAMPLES.exists() and not (EXAMPLES / "README.md").exists():
shutil.rmtree(EXAMPLES)
if not EXAMPLES.exists():
_("git", "clone", "https://github.com/jupyterlab/extension-examples.git")

print("JupyterLab with @jupyterlab/plugin-playground is ready to run with:\n")
print("\tjupyter lab\n")
76 changes: 76 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,89 @@
html_css_files = ["custom.css"]


def _ensure_extension_examples(root):
import subprocess

examples = root / "extension-examples"
if (examples / "README.md").exists():
return examples

if (root / ".git").exists():
try:
subprocess.check_call(
[
"git",
"submodule",
"update",
"--init",
"--recursive",
"extension-examples",
],
cwd=str(root),
)
except subprocess.CalledProcessError as exc:
raise RuntimeError(
"Failed to initialize the 'extension-examples' submodule."
) from exc

if (examples / "README.md").exists():
return examples

raise RuntimeError(
"Submodule update completed but 'extension-examples' was not found."
)

raise RuntimeError(
"Missing 'extension-examples'. Build from a git checkout with submodules "
"initialized."
)


def _sync_examples_to_lite_contents(root):
import shutil

examples = _ensure_extension_examples(root)
lite_examples_root = root / "docs" / "content" / "extension-examples"
if lite_examples_root.exists():
shutil.rmtree(lite_examples_root)
lite_examples_root.mkdir(parents=True, exist_ok=True)

ignored = shutil.ignore_patterns(
".git",
"node_modules",
"lib",
"dist",
".ipynb_checkpoints",
)

copied_count = 0
for example_dir in sorted(examples.iterdir()):
if not example_dir.is_dir() or example_dir.name.startswith("."):
continue

src_dir = example_dir / "src"
if not ((src_dir / "index.ts").exists() or (src_dir / "index.js").exists()):
continue

shutil.copytree(
example_dir,
lite_examples_root / example_dir.name,
ignore=ignored,
)
copied_count += 1

print(f"Copied {copied_count} extension examples into docs/content for Lite.")


def on_config_inited(*args):
import sys
import subprocess
from pathlib import Path

HERE = Path(__file__)
ROOT = HERE.parent.parent
_sync_examples_to_lite_contents(ROOT)

subprocess.check_call(["jlpm"], cwd=str(ROOT))
subprocess.check_call(["jlpm", "build"], cwd=str(ROOT))

Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module.exports = [
'src/modules.ts',
'docs/content/**',
'docs/_build/**',
'extension-examples/**',
'ui-tests/test-results/**'
]
},
Expand Down
1 change: 1 addition & 0 deletions extension-examples
Submodule extension-examples added at 31dccf
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"eslint:check": "eslint . --cache",
"lint": "jlpm prettier && jlpm eslint",
"lint:check": "jlpm prettier:check && jlpm eslint:check",
"check:extension-examples": "node scripts/check-extension-examples.js",
"prettier": "jlpm prettier:base --write --list-different",
"prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"",
"prettier:check": "jlpm prettier:base --check",
Expand All @@ -65,6 +66,7 @@
},
"devDependencies": {
"@eslint/js": "^9.39.3",
"@jupyter-notebook/application": "^7.0.0",
"@jupyter/collaborative-drive": "^4.2.1",
"@jupyter/docprovider": "^4.1.1",
"@jupyter/eslint-plugin": "^0.0.1",
Expand Down Expand Up @@ -97,6 +99,7 @@
"@jupyterlab/tooltip": "^4.5.5",
"@jupyterlab/workspaces": "^4.5.5",
"@lumino/datagrid": "^2.0.0",
"@rjsf/utils": "^5.13.4",
"@types/codemirror": "^5.6.20",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
Expand All @@ -108,7 +111,8 @@
"npm-run-all": "^4.1.5",
"prettier": "^2.8.0",
"rimraf": "^3.0.2",
"typescript": "~5.5.4"
"typescript": "~5.5.4",
"yjs": "^13.5.40"
},
"resolutions": {
"globals": "13.24.0",
Expand Down
Loading
Loading