<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - Anuj Singh</title><link href="https://jasongrout.github.io/medium-archive/pelican/" rel="alternate"/><link href="https://jasongrout.github.io/medium-archive/pelican/feeds/author-anuj-singh.atom.xml" rel="self"/><id>https://jasongrout.github.io/medium-archive/pelican/</id><updated>2026-05-28T18:07:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>A User’s Journey with Plugin Playground: From First Idea to Installable JupyterLab Extension.</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/" rel="alternate"/><published>2026-05-28T17:45:00+00:00</published><updated>2026-05-28T18:07:00+00:00</updated><author><name>Anuj Singh</name></author><id>tag:jasongrout.github.io,2026-05-28:/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/</id><summary type="html">&lt;p&gt;We are excited to announce the 1.0 release of JupyterLab Plugin Playground, allowing you to seamlessly experiment with the creation of…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We are excited to announce the 1.0 release of JupyterLab Plugin Playground, allowing you to seamlessly experiment with the creation of Jupyter Notebook and JupyterLab plugins to add any functionality you may desire. To install:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install jupyterlab-plugin-playground
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or use it from Binder or JupyterLite without installing anything by clicking one of these links: &lt;a href="https://mybinder.org/v2/gh/jupyterlab/plugin-playground/main?urlpath=lab"&gt;Binder (Lab)&lt;/a&gt;, &lt;a href="https://mybinder.org/v2/gh/jupyterlab/plugin-playground/main?urlpath=tree"&gt;Binder (Notebook v7)&lt;/a&gt;, &lt;a href="https://jupyterlab-plugin-playground.readthedocs.io/en/latest/lite/lab/"&gt;JupyterLite (Lab)&lt;/a&gt;, &lt;a href="https://jupyterlab-plugin-playground.readthedocs.io/en/latest/lite/tree/"&gt;JupyterLite (Notebook v7)&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="motivation"&gt;&lt;strong&gt;Motivation&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Building a JupyterLab extension has traditionally required setting up a complex local environment: installing dependencies from disjoint ecosystems (Node.js and Python), configuring tooling, and hunting down the right documentation. The goal of the Playground is to eliminate that setup overhead and streamline extension development. By bringing the runtime, documentation, and the context required for extension development into a single browser tab, you can go from idea to working extension without any local setup required.&lt;/p&gt;
&lt;h2 id="journey-at-a-glance"&gt;&lt;strong&gt;Journey At a Glance&lt;/strong&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Start with a tiny extension idea that is easy to verify visually.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Choose a build path: &lt;strong&gt;Manual&lt;/strong&gt; or &lt;strong&gt;AI-assisted&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Load&lt;/strong&gt; and iterate inside JupyterLab until behavior is stable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build a second extension to prove the workflow is repeatable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Share plugin files/packages for product and engineering review.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export as a &lt;strong&gt;wheel(.whl)&lt;/strong&gt; and validate installation in a clean Binder runtime.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export as a &lt;strong&gt;zip&lt;/strong&gt; and move the scaffold into a normal GitHub repository.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="10-pick-your-starting-path"&gt;&lt;strong&gt;1.0: Pick Your Starting Path&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;At the beginning, choose the path that matches your confidence level and speed needs. If this is your first time with Plugin Playground, you can optionally run &lt;code&gt;Take the Tour&lt;/code&gt; from the Launcher or Command Palette for quick orientation before starting.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Start from File&lt;/code&gt; If you already know the APIs you need and want direct control.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Build with AI&lt;/code&gt; If you know the intended behavior but want a first draft quickly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both paths converge to the same shipping workflow later.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/images/001-1_ejDftHO8TiktbPMim-EVqA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="20-build-extension-1-manually"&gt;&lt;strong&gt;2.0: Build Extension #1 Manually&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;For the first pass, we will create something small and observable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our Goal&lt;/strong&gt;: add a command named &lt;code&gt;Toggle Right Sidebar&lt;/code&gt; to the command palette.&lt;/p&gt;
&lt;h3 id="21-create-the-plugin-file"&gt;&lt;strong&gt;2.1: Create the plugin file&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Launch &lt;strong&gt;Start from File&lt;/strong&gt;. This comes with a dummy template.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;JupyterFrontEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;JupyterFrontEndPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@jupyterlab/application&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JupyterFrontEndPlugin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello-world:plugin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;autoStart&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JupyterFrontEnd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="22-fill-the-missing-pieces-using-the-right-sidebar"&gt;&lt;strong&gt;2.2: Fill the missing pieces using the right sidebar&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now build the plugin in order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;Tokens&lt;/code&gt;section in the right sidebar, search &lt;code&gt;ICommandPalette&lt;/code&gt; and click insert ( + icon ), so Plugin Playground adds token import and dependency wiring.&lt;/li&gt;
&lt;li&gt;Add a simple command skeleton inside the &lt;strong&gt;activate&lt;/strong&gt; function:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commandID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my-first-playground-plugin:toggle-sidebar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commandID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Toggle Right Sidebar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;// place cursor here&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="3"&gt;
&lt;li&gt;In &lt;code&gt;Commands&lt;/code&gt;, search &lt;code&gt;application:toggle-right-area&lt;/code&gt;, place your cursor inside &lt;code&gt;execute&lt;/code&gt; of the added command ( see above ) and use &lt;code&gt;Insert in selection&lt;/code&gt; This adds at the cursor position:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;application:toggle-right-area&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Register the command in the palette:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;commandPalette&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;commandID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AAA&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After those edits, your file should look like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ICommandPalette&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@jupyterlab/apputils&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;JupyterFrontEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;JupyterFrontEndPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@jupyterlab/application&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JupyterFrontEndPlugin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ow"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hello-world:plugin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;autoStart&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;requires&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ICommandPalette&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;JupyterFrontEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commandPalette&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;ICommandPalette&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;commandID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;my-first-playground-plugin:toggle-sidebar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commandID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Toggle Right Sidebar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;application:toggle-right-area&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;commandPalette&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;commandID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AAA&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Uses the typed plugin template (&lt;code&gt;JupyterFrontEndPlugin&amp;lt;void&amp;gt;&lt;/code&gt;) and typed &lt;code&gt;activate&lt;/code&gt; parameters. Imports &lt;code&gt;ICommandPalette&lt;/code&gt; so the plugin can add entries to the Command Palette.&lt;/li&gt;
&lt;li&gt;Registers a new command (&lt;code&gt;my-first-playground-plugin:toggle-sidebar&lt;/code&gt;) and executes &lt;code&gt;application:toggle-right-area&lt;/code&gt; when your command runs.&lt;/li&gt;
&lt;li&gt;Adds your command to the palette under the category &lt;code&gt;AAA&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="23-load-it-and-verify-behavior"&gt;&lt;strong&gt;2.3: Load it and verify behavior&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Click &lt;strong&gt;Load Current File As Extension or click the run button in the toolbar&lt;/strong&gt;, and open Command Palette.&lt;/p&gt;
&lt;p&gt;When you run &lt;strong&gt;Load Current File As Extension&lt;/strong&gt;, Plugin Playground compiles the active file and registers the plugin object into the live JupyterLab session. During iteration, if a plugin with the same &lt;strong&gt;id&lt;/strong&gt; already exists, Plugin Playground attempts to deactivate and replace it so reload loops stay fast.&lt;/p&gt;
&lt;h3 id="expected-result"&gt;&lt;strong&gt;Expected result&lt;/strong&gt;:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Toggle Right Sidebar&lt;/strong&gt; appears in the command palette ( open command palette by going through &lt;strong&gt;view -&amp;gt; Activate command palette&lt;/strong&gt; or Press &lt;strong&gt;Accel&lt;/strong&gt; + &lt;strong&gt;shift&lt;/strong&gt; + &lt;strong&gt;C&lt;/strong&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/images/002-1_RXMnfrUXeu1q7AkukZEF_A.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Running it hides/shows the JupyterLab right sidebar.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="30-build-with-ai"&gt;&lt;strong&gt;3.0: Build with AI&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If you want a faster draft, use AI assistance for the same extension workflow.&lt;/p&gt;
&lt;p&gt;Goal: add the &lt;strong&gt;Show Active Notebook Cell Count&lt;/strong&gt; command with a friendly no-notebook fallback.&lt;/p&gt;
&lt;h3 id="31-give-ai-a-precise-request"&gt;&lt;strong&gt;3.1: Give AI a precise request&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Use &lt;strong&gt;Build with AI&lt;/strong&gt; and give a prompt:&lt;/p&gt;
&lt;p&gt;for example, something like:-&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Create a JupyterLab plugin named &amp;quot;active-notebook-cell-counter&amp;quot;.
Add a command &amp;quot;Show Active Notebook Cell Count&amp;quot;.
If no notebook is active, show a friendly dialog message.
If a notebook is active, show the current cell count in a dialog.
Register the command in the command palette under category &amp;quot;Playground Demo&amp;quot;.
Export default plugin object.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="what-this-block-does"&gt;What this block does:&lt;/h3&gt;
&lt;p&gt;This gives AI a concrete plugin goal, command name, and fallback behavior requirements.&lt;/p&gt;
&lt;h3 id="32-use-ai-deterministic-inserts-together"&gt;&lt;strong&gt;3.2: Use AI + deterministic inserts together&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In the &lt;strong&gt;Commands&lt;/strong&gt; tab:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Insert in selection&lt;/strong&gt; for predictable boilerplate insertion.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prompt AI to insert&lt;/strong&gt; when placement context is tricky.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;{n}&lt;/strong&gt; when you need argument shape clarity ( Inspect the command signature before insertion: it shows expected argument names/types and return details).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Use &lt;strong&gt;Insert in selection&lt;/strong&gt; for predictable snippets, and switch to AI when insertion location or code adaptation depends on the surrounding context.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/images/003-1_HAs9F-B9l77KolI3ZZTH3g.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h3 id="40-iterate-faster-before-handoff"&gt;&lt;strong&gt;4.0: Iterate Faster Before Handoff&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Once the extensions run, enable &lt;strong&gt;Run on save&lt;/strong&gt;(from the toolbar or from the settings) while polishing labels, command categories, and small behaviour details.&lt;/p&gt;
&lt;p&gt;With &lt;strong&gt;Run on save&lt;/strong&gt;, each file save triggers a reload loop for that plugin file, which makes label tweaks, command grouping changes, and message-copy iteration much faster than manual reload cycles. And with enabling the global setting (&lt;strong&gt;Load as extension on save&lt;/strong&gt;), this behaviour is present for all files by default.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/a-users-journey-with-plugin-playground-from-first-idea/images/004-1_wfxS4HhzRfU5-bbKYfPonA.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;h3 id="50-share-for-review"&gt;&lt;strong&gt;5.0: Share for Review&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Before packaging, you can ask a teammate to review behaviour or just share links for the extension for a quick look using:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Share a single file&lt;/strong&gt; when sharing is about one plugin file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Share a package&lt;/strong&gt; when your logic spans several files.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps review lightweight: collaborators can inspect and discuss behaviour before you commit to repository structure, CI, and release wiring.&lt;/p&gt;
&lt;h3 id="60-export-as-wheel-and-validate-in-binder"&gt;&lt;strong&gt;6.0: Export as Wheel and Validate in Binder&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Now test as an installable artifact in a separate environment.&lt;/p&gt;
&lt;h3 id="61-export-whl"&gt;&lt;strong&gt;6.1: Export &lt;code&gt;.whl&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;From the export dropdown ( in toolbar ), choose &lt;strong&gt;Export as Python package (.whl)&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id="62-install-in-binder"&gt;&lt;strong&gt;6.2: Install in Binder&lt;/strong&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open JupyterLab on Binder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Upload the downloaded wheel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and run:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;force-reinstall&lt;span class="w"&gt; &lt;/span&gt;./your_exported_package.whl
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="what-this-block-does-1"&gt;What this block does:&lt;/h3&gt;
&lt;p&gt;Installs the wheel you just exported from Plugin Playground into Binder. (Uses &lt;strong&gt;— force-reinstall&lt;/strong&gt; so Binder replaces any previously installed copy with your newest build). Then refresh JupyterLab and verify commands.&lt;/p&gt;
&lt;h3 id="70-export-as-zip-and-create-a-github-repository"&gt;&lt;strong&gt;7.0: Export as Zip and Create a GitHub Repository&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;After runtime validation, move to normal engineering workflows.&lt;/p&gt;
&lt;h3 id="71-export-zip"&gt;&lt;strong&gt;7.1: Export &lt;code&gt;.zip&lt;/code&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Choose &lt;code&gt;**Export as archive (.zip)**&lt;/code&gt; from the toolbar export dropdown.&lt;/p&gt;
&lt;h3 id="72-bootstrap-repository"&gt;&lt;strong&gt;7.2: Bootstrap repository&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Unzip locally, create an empty GitHub repo, then run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;init&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Initial extension scaffold from Plugin Playground&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/&amp;lt;your-org-or-user&amp;gt;/my-jlab-extension.git&lt;/span&gt;
&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id="what-this-block-does-2"&gt;What this block does:&lt;/h3&gt;
&lt;p&gt;Initializes a new Git repository from the exported scaffold and creates an initial commit that captures the generated extension baseline.&lt;/p&gt;
&lt;p&gt;Now your prototype is in a standard repo flow with PRs, CI, and release automation.&lt;/p&gt;
&lt;h3 id="whats-next"&gt;What’s Next&lt;/h3&gt;
&lt;p&gt;Plugin Playground is still evolving. We’re actively working on the &lt;a href="https://github.com/jupyterlab/plugin-playground/pull/225"&gt;LSP integration&lt;/a&gt; for JupyterLite to bring code intelligence autocomplete and diagnostics directly into the editor, an &lt;a href="https://github.com/jupyterlab/plugin-playground/pull/231"&gt;“Ask AI” button on log errors&lt;/a&gt; that lets you send error context directly into the AI chat for instant debugging help. In the future, we would like to explore &lt;a href="https://github.com/jupyterlab/plugin-playground/issues/139"&gt;git integration&lt;/a&gt; to snapshots every successful load, paired with a &lt;a href="https://github.com/jupyterlab/plugin-playground/issues/140"&gt;built-in diff viewer&lt;/a&gt; to compare any snapshot against your current file. If you have any suggestions or encounter any problems, please let us know by opening &lt;a href="https://github.com/jupyterlab/plugin-playground/issues"&gt;an issue on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="acknowledgements"&gt;&lt;strong&gt;Acknowledgements&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;We are grateful to the &lt;a href="https://jupyterfoundation.org/join/"&gt;Jupyter Foundation&lt;/a&gt; and its &lt;a href="https://jupyterfoundation.org/members/"&gt;members&lt;/a&gt; for sponsoring the development of Plugin Playground as part of &lt;a href="https://jupyterfoundation.org/community-funding-proposals/"&gt;Community Funding Proposals 2025&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We thank Jeremy Tuloup, Nick Bollweg, and Nicolas Brichet for providing inspiration, review, and advice across our work on the plugin and its dependencies. Finally, this work would not be possible without the &lt;a href="https://github.com/jupyterlab/plugin-playground/graphs/contributors?from=01%2F01%2F2020&amp;amp;to=18%2F12%2F2024"&gt;authors of earlier&lt;/a&gt; iterations of Plugin Playground.&lt;/p&gt;
&lt;h3 id="about-the-developers"&gt;&lt;strong&gt;About the&lt;/strong&gt; Developers&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/in/anuj-singh-2b2ab6206/"&gt;&lt;strong&gt;Anuj Singh&lt;/strong&gt;&lt;/a&gt; is an OSS intern at OpenTeams. Anuj implemented Plugin Playground v1.0.0 and contributed to its dependencies during his internship at &lt;a href="https://openteams.com/"&gt;OpenTeams&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/in/smera-goel/"&gt;&lt;strong&gt;Smera Goel&lt;/strong&gt;&lt;/a&gt; is a UI/UX designer at &lt;a href="https://quansight.com/"&gt;Quansight&lt;/a&gt;. Smera contributed to the user experience and design direction of Plugin Playground.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.linkedin.com/in/michal-krassowski/"&gt;&lt;strong&gt;Michał Krassowski&lt;/strong&gt;&lt;/a&gt; is a Senior Software Engineer at &lt;a href="https://openteams.com/"&gt;OpenTeams&lt;/a&gt;. Mike provided guidance and direction for the development of the Plugin Playground.&lt;/p&gt;
&lt;h3 id="further-reading"&gt;Further reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Blog post on &lt;a href="https://openteams.com/plugin-playground-ai-integration/"&gt;how and why we integrated AI functions in the playground&lt;/a&gt; extension&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyterlab/plugin-playground/blob/main/CHANGELOG.md"&gt;Changelog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jupyterlab/plugin-playground/blob/main/README.md"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content><category term="AI"/><category term="extensions"/><category term="JupyterLab"/><category term="JupyterLite"/></entry></feed>