Skip to content

Commit ecded4f

Browse files
committed
Merge main into addJSKernel
2 parents 08531a5 + 6084291 commit ecded4f

12 files changed

Lines changed: 1011 additions & 591 deletions

File tree

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77

88
| Try in your browser | Lab | Notebook v7 |
99
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
10-
| Binder (Powerful) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyterlab/plugin-playground/main?urlpath=lab) | [![Binder Notebook v7](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyterlab/plugin-playground/main?urlpath=tree) |
10+
| Binder (Powerful) | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyterlab/plugin-playground/binder-stable?urlpath=lab) | [![Binder Notebook v7](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyterlab/plugin-playground/binder-stable?urlpath=tree) |
1111
| JupyterLite (Instant) | [![JupyterLite](https://jupyterlite.rtfd.io/en/latest/_static/badge-launch.svg)](https://jupyterlab-plugin-playground.readthedocs.io/en/latest/lite/lab/) | [![JupyterLite Notebook v7](https://jupyterlite.rtfd.io/en/latest/_static/badge-launch.svg)](https://jupyterlab-plugin-playground.readthedocs.io/en/latest/lite/tree/) |
1212

13+
<!-- Binder links above are intentionally pointed at the `binder-stable` branch with pinned Binder dependencies; update that branch only after validating Binder builds. -->
14+
1315
A JupyterLab extension to write and load simple JupyterLab plugins inside JupyterLab.
1416

1517
## Install
@@ -30,7 +32,7 @@ Plugin Playground is built to keep the full plugin prototyping workflow inside J
3032
![Export format dropdown in editor toolbar](docs/images/readme/editor-toolbar-export-dropdown.png)
3133
![Share target dropdown in editor toolbar](docs/images/readme/editor-toolbar-share-dropdown.png)
3234

33-
The right sidebar includes a single Plugin Playground panel with two collapsible sections. In **Extension Points**, the `Tokens` tab helps you discover available token strings and insert import/dependency updates, the `Commands` tab lets you search command IDs, inspect argument docs, and insert execution snippets (either directly or through AI-assisted prompt mode), and the `Packages` tab surfaces package docs plus npm and repository links for known modules.
35+
The right sidebar includes a single Plugin Playground panel with three collapsible sections. In **Extension Points**, the `Tokens` tab helps you discover available token strings and insert import/dependency updates, the `Commands` tab lets you search command IDs, inspect argument docs, and insert execution snippets (either directly or through AI-assisted prompt mode), and the `Packages` tab surfaces package docs plus npm and repository links for known modules.
3436

3537
![Extension Points token discovery and insertion actions](docs/images/readme/extension-points-tokens.png)
3638
![Extension Points command discovery and insertion actions](docs/images/readme/extension-points-commands.png)
@@ -40,6 +42,8 @@ The **Extension Examples** section lists discovered examples from `extension-exa
4042

4143
![Extension Examples section with code and README actions](docs/images/readme/extension-examples.png)
4244

45+
The **Currently Loaded Plugins** section lists plugins loaded by Plugin Playground in the current session, shows each plugin's source path, and lets you deactivate a selected plugin without clearing the rest of the session.
46+
4347
Command completion is also included for `app.commands.execute(...)` / `commands.execute(...)` in JavaScript and TypeScript editors, and Notebook v7 integrates `Start from File`, `Build with AI`, and `Take the Tour` into New-file flows so you can create starter plugin files from the tree interface.
4448

4549
The Launcher `Plugin Playground` section includes `Start from File`, `Build with AI`, and `Take the Tour` so first-time users can pick a guided flow quickly.
@@ -59,7 +63,7 @@ jlpm docs:screenshots
5963
3. Paste plugin code into the active editor.
6064
4. Run `Load Current File As Extension` from the editor toolbar or Command Palette.
6165
5. Use the `Run on save` icon button for fast iteration on one file.
62-
6. Use the sidebar to discover tokens, commands, packages, and extension examples.
66+
6. Use the sidebar to discover tokens, commands, packages, extension examples, and currently loaded playground plugins.
6367

6468
For extension examples availability:
6569

@@ -136,6 +140,8 @@ There are a few differences in how to write plugins in Plugin Playground compare
136140
- To load code from an external package, RequireJS is used (hidden behind ES module-compatible import syntax), so import statements may need explicit version or file paths.
137141
- In addition to JupyterLab and Lumino packages, only AMD modules can be imported; ES modules and modules compiled for Webpack/Node are not supported directly and can fail with `Uncaught SyntaxError: Unexpected token 'export'`.
138142
- The playground can import relative files (`.ts`, `.tsx`, `.js`, and `.css`), load SVG as strings, and load settings schema from `package.json` (`jupyterlab.schemaDir`) with `plugin.json` fallback for single-plugin prototyping.
143+
- For multi-file prototypes, create a package root with `package.json` before adding files under `src/`, `schema/`, or `style/`. This lets `Share Package` and extension export find the whole plugin folder reliably.
144+
- Playground prototypes cannot replace existing JupyterLab commands by reusing their command IDs. Register your own command ID and add it to the relevant menu, toolbar, launcher, palette, or cell toolbar instead.
139145

140146
### Migrating from version 0.3.0
141147

@@ -172,6 +178,10 @@ Plugin Playground supports AI-assisted extension prototyping in both JupyterLite
172178
- [JupyterLite AI documentation](https://jupyterlite-ai.readthedocs.io/en/latest/)
173179
- [Plugin authoring skill for agents](_agents/skills/plugin-authoring/SKILL.md)
174180

181+
### JS Logs Ask AI
182+
183+
Plugin Playground adds an `Ask AI` button to JS Logs entries so you can send error text to JupyterLite AI chat for debugging context. The button appears only on `error`/`critical` entries that include text content, and only when JupyterLite AI is available and a provider is configured.
184+
175185
### Command Insert Modes (Default + AI Prompt)
176186

177187
In the `Commands` tab, each command row includes a split `+` action and a mode dropdown:

_agents/skills/plugin-authoring/SKILL.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,23 @@ Produce working plugin code that can be loaded with `plugin-playground:load-as-e
3737
- If the user does not already have a plugin file open or specified, run `plugin-playground:create-new-plugin` with a meaningful `path` argument (for example `app.commands.execute('plugin-playground:create-new-plugin', { path: 'status-indicator.ts' })`) instead of relying on untitled defaults.
3838
- Start from the generated TypeScript scaffold and adapt it.
3939
- Plugin Playground supports schema-backed settings during prototyping (`package.json` with `jupyterlab.schemaDir`, with `plugin.json` fallback for single-plugin cases).
40+
- For any plugin with multiple files, settings schema, CSS, SVG, or other local assets, create a package root up front (for example `my-extension/src/index.ts`) and include a minimal `package.json` in `my-extension/` so `Share Package` and extension export operate on the full folder.
41+
- When creating nested files through AI file commands, create the parent directories first and verify the files appear in the file browser before continuing.
4042
- Focus on TypeScript/TSX plugin code. Do not scaffold Python projects (`pyproject.toml`, Python package layout) unless explicitly requested.
4143

44+
Minimal package metadata for Playground prototypes:
45+
46+
```json
47+
{
48+
"name": "my-extension",
49+
"version": "0.1.0",
50+
"jupyterlab": {
51+
"extension": true,
52+
"schemaDir": "schema"
53+
}
54+
}
55+
```
56+
4257
2. Discover available extension points
4358

4459
- Run `plugin-playground:list-tokens` to get available tokens.
@@ -57,7 +72,8 @@ Produce working plugin code that can be loaded with `plugin-playground:load-as-e
5772

5873
- Start from a minimal plugin shape (`id`, `autoStart`, `activate`).
5974
- Add `requires` tokens only after confirming availability from step 2.
60-
- Add commands with stable IDs (`<namespace>:<action>`).
75+
- Add commands with stable, unique IDs (`<namespace>:<action>`).
76+
- Do not overwrite or reuse built-in JupyterLab command IDs. Plugin Playground skips duplicate command registrations; register a separate command and add it to the relevant palette, menu, toolbar, launcher, or cell toolbar instead.
6177
- Use one or more `.ts`/`.tsx` files as needed as complexity grows.
6278

6379
5. Load and iterate

binder/environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dependencies:
2121
# additional packages for demos
2222
# - ipywidgets
2323
- jupyterlab-lsp
24-
- jupyterlab-js-logs>=1.2,<2
24+
- jupyterlab-js-logs>=1.3.1,<2
2525
- jupyterlab-tour>=4,<5
2626
# use the same AI package in Binder and JupyterLite deployments
2727
- jupyterlite-ai>=0.15,<1

docs/environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ dependencies:
1111
- ipywidgets>=8.0,<9
1212
- jupyterlab>=4.0.0,<5
1313
- jupyterlab-lsp>=5.2.0,<6
14-
- jupyterlab-js-logs>=1.2,<2
14+
- jupyterlab-js-logs>=1.3.1,<2
1515
- jupyterlab-tour>=4,<5
1616
- jupyterlab-language-pack-fr-FR
1717
- jupyterlab-language-pack-zh-CN

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"@jupyterlab/lsp": "^4.5.5",
7171
"@jupyterlab/settingregistry": "^4.5.5",
7272
"@typescript/vfs": "^1.6.4",
73-
"jupyterlab-js-logs": "^1.2.0",
73+
"jupyterlab-js-logs": "^1.3.1",
7474
"raw-loader": "^4.0.2",
7575
"requirejs": "^2.3.6",
7676
"semver": "^7.7.4",
@@ -138,15 +138,18 @@
138138
"extension": true,
139139
"outputDir": "jupyterlab_plugin_playground/labextension",
140140
"schemaDir": "schema",
141-
"disabledExtensions": [
142-
"jupyterlab-tour:default-tours"
143-
],
144141
"sharedPackages": {
145142
"@jupyterlab/lsp": {
146143
"bundled": false,
147144
"singleton": true,
148145
"strictVersion": false
146+
},
147+
"jupyterlab-js-logs": {
148+
"singleton": true
149149
}
150-
}
150+
},
151+
"disabledExtensions": [
152+
"jupyterlab-tour:default-tours"
153+
]
151154
}
152155
}

src/contents.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export namespace ContentUtils {
6262
};
6363

6464
export type IFileModel = Contents.IModel & {
65-
type: 'file';
65+
type: 'file' | 'notebook';
6666
content: unknown;
6767
format?: string | null;
6868
};
@@ -194,23 +194,27 @@ export namespace ContentUtils {
194194
const model = await getContentsModel(serviceManager, candidatePath, {
195195
content: true
196196
});
197-
if (!model || model.type !== 'file') {
197+
if (!model || (model.type !== 'file' && model.type !== 'notebook')) {
198198
continue;
199199
}
200200
if (model.content !== null) {
201201
return model as IFileModel;
202202
}
203203

204-
const textModel = await getContentsModel(serviceManager, candidatePath, {
205-
content: true,
206-
format: 'text'
207-
});
204+
const fallbackModel = await getContentsModel(
205+
serviceManager,
206+
candidatePath,
207+
{
208+
content: true,
209+
format: model.type === 'notebook' ? 'json' : 'text'
210+
}
211+
);
208212
if (
209-
textModel &&
210-
textModel.type === 'file' &&
211-
textModel.content !== null
213+
fallbackModel &&
214+
(fallbackModel.type === 'file' || fallbackModel.type === 'notebook') &&
215+
fallbackModel.content !== null
212216
) {
213-
return textModel as IFileModel;
217+
return fallbackModel as IFileModel;
214218
}
215219
}
216220
return null;

0 commit comments

Comments
 (0)