extension_dev.rst 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. .. _developer_extensions:
  2. Extension Developer Guide
  3. -------------------------
  4. JupyterLab can be extended in four ways via:
  5. - **application plugins (top level):** Application plugins extend the
  6. functionality of JupyterLab itself.
  7. - **mime renderer extensions (top level):** Mime Renderer extensions are
  8. a convenience for creating an extension that can render mime data and
  9. potentially render files of a given type.
  10. - **theme extensions (top level):** Theme extensions allow you to customize the appearance of
  11. JupyterLab by adding your own fonts, CSS rules, and graphics to the application.
  12. - **document widget extensions (lower level):** Document widget extensions
  13. extend the functionality of document widgets added to the
  14. application, and we cover them in :ref:`documents`.
  15. A JupyterLab application is comprised of:
  16. - A core Application object
  17. - Plugins
  18. Starting in JupyterLab 3.0, extensions are distributed as ``pip`` or
  19. ``conda`` packages that contain federated JavaScript bundles. You can write extensions in JavaScript or any language that compiles to JavaScript. We recommend writing extensions in `TypeScript <https://www.typescriptlang.org/>`_, which is used for the JupyterLab core extensions and many popular community extensions. You use our build tool to generate the bundles that are shipped with the package, typically through a cookiecutter.
  20. Goals of the Federated Extension System
  21. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  22. - Users should be able to install and use extensions without requiring ``node`` or a build step
  23. - Extension authors should be able to easily build and distribute extensions
  24. - The existing capabilities of built-in extensions should still work
  25. - Administrators should retain the ability to set global configuration and packages where possible
  26. - Federated extensions should layer on top of existing extensions similar to how ``pip install --user`` works
  27. - Extensions should be discoverable
  28. Implementation
  29. ~~~~~~~~~~~~~~
  30. - We provide a ``jupyter labextension build`` script that is used to build federated bundles
  31. - The command produces a set of static assets that are shipped along with a package (notionally on ``pip``/``conda``)
  32. - It is a Python cli so that it can use the dependency metadata from the active JupyterLab
  33. - The assets include a module federation ``remoteEntry.*.js``, generated bundles, and some other files that we use
  34. - ``package.json`` is the original ``package.json`` file that we use to gather metadata about the package, with some included build metadata
  35. - we use the previously existing ``@jupyterlab/builder -> build`` to generate the ``imports.css``, ``schemas`` and ``themes`` file structure
  36. - We provide a schema for the valid ``jupyterlab`` metadata for an extension's ``package.json`` describing the available options
  37. - We provide a ``labextensions`` handler in ``jupyterlab_server`` that loads static assets from ``labextensions`` paths, following a similar logic to how ``nbextensions`` are discovered and loaded from disk
  38. - The ``settings`` and ``themes`` handlers in ``jupyterlab_server`` has been updated to load from the new ``labextensions`` locations, favoring the federated extension locations over the bundled ones
  39. - A ``labextension develop`` command has been added to install an in-development extension into JupyterLab. The default behavior is to create a symlink in the ``sys-prefix/share/jupyter/labextensions/package-name`` to the static directory of the extension
  40. - We provide a ``cookiecutter`` that handles all of the scaffolding for an extension author, including the shipping of ``data_files`` so that when the user installs the package, the static assets end up in ``share/jupyter/labextensions``
  41. - We handle disabling of lab extensions using a trait on the ``LabApp`` class, so it can be set by admins and overridden by users. Extensions are automatically enabled when installed, and must be explicitly disabled. The disabled config can consist of a package name or a plugin regex pattern
  42. - Extensions can provide ``disabled`` metadata that can be used to replace an entire extension or individual plugins
  43. - ``page_config`` and ``overrides`` are also handled with traits so that admins can provide defaults and users can provide overrides
  44. - We provide a script to update extensions: ``python -m jupyterlab.upgrade_extension``
  45. - We update the ``extension-manager`` to target metadata on ``pypi``/``conda`` and consume those packages.
  46. Tools
  47. ~~~~~
  48. - ``jupyter labexension build`` python command line tool
  49. - ``jupyter labextension develop`` python command line tool
  50. - ``python -m jupyterlab.upgrade_extension`` python command line tool
  51. - ``cookiecutter`` for extension authors
  52. Workflow for extension authors
  53. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  54. - Use the ``cookiecutter`` to create the extension
  55. - Run ``jupyter labextension develop`` to build and symlink the files
  56. - Run ``jupyter labextension watch`` to start watching
  57. - Run ``jupyter lab``
  58. - Make changes to source
  59. - Refresh the application page
  60. - When finished, publish the package to ``pypi``/``conda``
  61. .. note::
  62. These docs are under construction as we iterate and update tutorials and cookiecutters.
  63. Tutorials
  64. ~~~~~~~~~
  65. We provide a set of guides to get started writing third-party extensions for JupyterLab:
  66. - :ref:`extension_tutorial`: An in-depth tutorial to learn how to make a simple JupyterLab extension.
  67. - The `JupyterLab Extension Examples Repository <https://github.com/jupyterlab/extension-examples>`_: A short tutorial series
  68. to learn how to develop extensions for JupyterLab, by example.
  69. - :ref:`developer-extension-points`: A list of the most common JupyterLab extension points.
  70. Cookiecutters
  71. ~~~~~~~~~~~~~
  72. We provide several cookiecutters to create JupyterLab plugin extensions:
  73. - `extension-cookiecutter-ts <https://github.com/jupyterlab/extension-cookiecutter-ts>`_: Create a JupyterLab extension in TypeScript
  74. - `extension-cookiecutter-js <https://github.com/jupyterlab/extension-cookiecutter-js>`_: Create a JupyterLab extension in JavaScript
  75. - `mimerender-cookiecutter-ts <https://github.com/jupyterlab/mimerender-cookiecutter-ts>`_: Create a MIME Renderer JupyterLab extension in TypeScript
  76. - `theme-cookiecutter <https://github.com/jupyterlab/theme-cookiecutter>`_: Create a new theme for JupyterLab
  77. API Documentation
  78. ~~~~~~~~~~~~~~~~~
  79. If you are looking for lower level details on the JupyterLab and Lumino API:
  80. - `JupyterLab API Documentation <https://jupyterlab.github.io/jupyterlab/>`_
  81. - `Lumino API Documentation <https://jupyterlab.github.io/lumino/>`_
  82. Plugins
  83. ~~~~~~~
  84. A plugin adds a core functionality to the application:
  85. - A plugin can require other plugins for operation.
  86. - A plugin is activated when it is needed by other plugins, or when
  87. explicitly activated.
  88. - Plugins require and provide ``Token`` objects, which are used to
  89. provide a typed value to the plugin's ``activate()`` method.
  90. - The module providing plugin(s) must meet the
  91. `JupyterLab.IPluginModule <https://jupyterlab.github.io/jupyterlab/interfaces/_application_src_index_.jupyterlab.ipluginmodule.html>`__
  92. interface, by exporting a plugin object or array of plugin objects as
  93. the default export.
  94. The default plugins in the JupyterLab application include:
  95. - `Terminal <https://github.com/jupyterlab/jupyterlab/blob/master/packages/terminal-extension/src/index.ts>`__
  96. - Adds the ability to create command prompt terminals.
  97. - `Shortcuts <https://github.com/jupyterlab/jupyterlab/blob/master/packages/shortcuts-extension/src/index.ts>`__
  98. - Sets the default set of shortcuts for the application.
  99. - `Images <https://github.com/jupyterlab/jupyterlab/blob/master/packages/imageviewer-extension/src/index.ts>`__
  100. - Adds a widget factory for displaying image files.
  101. - `Help <https://github.com/jupyterlab/jupyterlab/blob/master/packages/help-extension/src/index.tsx>`__
  102. - Adds a side bar widget for displaying external documentation.
  103. - `File
  104. Browser <https://github.com/jupyterlab/jupyterlab/blob/master/packages/filebrowser-extension/src/index.ts>`__
  105. - Creates the file browser and the document manager and the file
  106. browser to the side bar.
  107. - `Editor <https://github.com/jupyterlab/jupyterlab/blob/master/packages/fileeditor-extension/src/index.ts>`__
  108. - Add a widget factory for displaying editable source files.
  109. - `Console <https://github.com/jupyterlab/jupyterlab/blob/master/packages/console-extension/src/index.ts>`__
  110. - Adds the ability to launch Jupyter Console instances for
  111. interactive kernel console sessions.
  112. Here is a dependency graph for the core JupyterLab components: |dependencies|
  113. .. danger::
  114. Installing an extension allows for arbitrary code execution on the
  115. server, kernel, and in the client's browser. You should therefore
  116. take steps to protect against malicious changes to your extension's
  117. code. This includes ensuring strong authentication for your PyPI
  118. account.
  119. Application Object
  120. ~~~~~~~~~~~~~~~~~~
  121. A Jupyter front-end application object is given to each plugin in its
  122. ``activate()`` function. The application object has:
  123. - ``commands`` - an extensible registry used to add and execute commands in the application.
  124. - ``commandLinker`` - used to connect DOM nodes with the command registry so that clicking on them executes a command.
  125. - ``docRegistry`` - an extensible registry containing the document types that the application is able to read and render.
  126. - ``restored`` - a promise that is resolved when the application has finished loading.
  127. - ``serviceManager`` - low-level manager for talking to the Jupyter REST API.
  128. - ``shell`` - a generic Jupyter front-end shell instance, which holds the user interface for the application.
  129. Jupyter Front-End Shell
  130. ~~~~~~~~~~~~~~~~~~~~~~~
  131. The Jupyter front-end
  132. `shell <https://jupyterlab.github.io/jupyterlab/interfaces/_application_src_index_.jupyterfrontend.ishell.html>`__
  133. is used to add and interact with content in the application. The ``IShell``
  134. interface provides an ``add()`` method for adding widgets to the application.
  135. In JupyterLab, the application shell consists of:
  136. - A ``top`` area for things like top level menus and toolbars.
  137. - ``left`` and ``right`` side bar areas for collapsible content.
  138. - A ``main`` work area for user activity.
  139. - A ``bottom`` area for things like status bars.
  140. - A ``header`` area for custom elements.
  141. Lumino
  142. ~~~~~~~~
  143. The Lumino library is used as the underlying architecture of
  144. JupyterLab and provides many of the low level primitives and widget
  145. structure used in the application. Lumino provides a rich set of
  146. widgets for developing desktop-like applications in the browser, as well
  147. as patterns and objects for writing clean, well-abstracted code. The
  148. widgets in the application are primarily **Lumino widgets**, and
  149. Lumino concepts, like message passing and signals, are used
  150. throughout. **Lumino messages** are a *many-to-one* interaction that
  151. enables information like resize events to flow through the widget
  152. hierarchy in the application. **Lumino signals** are a *one-to-many*
  153. interaction that enable listeners to react to changes in an observed
  154. object.
  155. Extension Authoring
  156. ~~~~~~~~~~~~~~~~~~~
  157. An Extension is a valid `npm
  158. package <https://docs.npmjs.com/getting-started/what-is-npm>`__ that
  159. meets the following criteria:
  160. - Exports one or more JupyterLab plugins as the default export in its
  161. main file.
  162. - Has a ``jupyterlab`` key in its ``package.json`` which has
  163. ``"extension"`` metadata. The value can be ``true`` to use the main
  164. module of the package, or a string path to a specific module (e.g.
  165. ``"lib/foo"``). Example::
  166. "jupyterlab": {
  167. "extension": true
  168. }
  169. - It is also recommended to include the keyword ``jupyterlab-extension``
  170. in the ``package.json``, to aid with discovery (e.g. by the extension
  171. manager). Example::
  172. "keywords": [
  173. "jupyter",
  174. "jupyterlab",
  175. "jupyterlab-extension"
  176. ],
  177. While authoring the extension, you can use the command:
  178. .. code:: bash
  179. npm install # install npm package dependencies
  180. npm run build # optional build step if using TypeScript, babel, etc.
  181. jupyter labextension install # install the current directory as an extension
  182. This causes the builder to re-install the source folder before building
  183. the application files. You can re-build at any time using
  184. ``jupyter lab build`` and it will reinstall these packages.
  185. You can also link other local ``npm`` packages that you are working on
  186. simultaneously using ``jupyter labextension link``; they will be re-installed
  187. but not considered as extensions. Local extensions and linked packages are
  188. included in ``jupyter labextension list``.
  189. When using local extensions and linked packages, you can run the command
  190. ::
  191. jupyter lab --watch
  192. This will cause the application to incrementally rebuild when one of the
  193. linked packages changes. Note that only compiled JavaScript files (and
  194. the CSS files) are watched by the WebPack process. This means that if
  195. your extension is in TypeScript you'll have to run a ``jlpm run build``
  196. before the changes will be reflected in JupyterLab. To avoid this step
  197. you can also watch the TypeScript sources in your extension which is
  198. usually assigned to the ``tsc -w`` shortcut. If WebPack doesn't seem to
  199. detect the changes, this can be related to `the number of available watches <https://github.com/webpack/docs/wiki/troubleshooting#not-enough-watchers>`__.
  200. Note that the application is built against **released** versions of the
  201. core JupyterLab extensions. If your extension depends on JupyterLab
  202. packages, it should be compatible with the dependencies in the
  203. ``jupyterlab/static/package.json`` file. Note that building will always use the latest JavaScript packages that meet the dependency requirements of JupyterLab itself and any installed extensions. If you wish to test against a
  204. specific patch release of one of the core JupyterLab packages you can
  205. temporarily pin that requirement to a specific version in your own
  206. dependencies.
  207. If you must install an extension into a development branch of JupyterLab, you have to graft it into the source tree of JupyterLab itself. This may be done using the command
  208. ::
  209. jlpm run add:sibling <path-or-url>
  210. in the JupyterLab root directory, where ``<path-or-url>`` refers either
  211. to an extension ``npm`` package on the local file system, or a URL to a git
  212. repository for an extension ``npm`` package. This operation may be
  213. subsequently reversed by running
  214. ::
  215. jlpm run remove:package <extension-dir-name>
  216. This will remove the package metadata from the source tree and delete
  217. all of the package files.
  218. The package should export EMCAScript 6 compatible JavaScript. It can
  219. import CSS using the syntax ``require('foo.css')``. The CSS files can
  220. also import CSS from other packages using the syntax
  221. ``@import url('~foo/index.css')``, where ``foo`` is the name of the
  222. package.
  223. The following file types are also supported (both in JavaScript and
  224. CSS): ``json``, ``html``, ``jpg``, ``png``, ``gif``, ``svg``,
  225. ``js.map``, ``woff2``, ``ttf``, ``eot``.
  226. If your package uses any other file type it must be converted to one of
  227. the above types or `include a loader in the import statement <https://webpack.js.org/concepts/loaders/#inline>`__.
  228. If you include a loader, the loader must be importable at build time, so if
  229. it is not already installed by JupyterLab, you must add it as a dependency
  230. of your extension.
  231. If your JavaScript is written in any other dialect than
  232. EMCAScript 6 (2015) it should be converted using an appropriate tool.
  233. You can use Webpack to pre-build your extension to use any of it's features
  234. not enabled in our build configuration. To build a compatible package set
  235. ``output.libraryTarget`` to ``"commonjs2"`` in your Webpack configuration.
  236. (see `this <https://github.com/saulshanabrook/jupyterlab-webpack>`__ example repo).
  237. Another option to try out your extension with a local version of JupyterLab is to add it to the
  238. list of locally installed packages and to have JupyterLab register your extension when it starts up.
  239. You can do this by adding your extension to the ``jupyterlab.externalExtensions`` key
  240. in the ``dev_mode/package.json`` file. It should be a mapping
  241. of extension name to version, just like in ``dependencies``. Then run ``jlpm run integrity``
  242. and these extensions should be added automatically to the ``dependencies`` and pulled in.
  243. When you then run ``jlpm run build && jupyter lab --dev`` or ``jupyter lab --dev --watch`` this extension
  244. will be loaded by default. For example, this is how you can add the Jupyter Widgets
  245. extensions:
  246. ::
  247. "externalExtensions": {
  248. "@jupyter-widgets/jupyterlab-manager": "2.0.0"
  249. },
  250. If you publish your extension on ``npm.org``, users will be able to install
  251. it as simply ``jupyter labextension install <foo>``, where ``<foo>`` is
  252. the name of the published ``npm`` package. You can alternatively provide a
  253. script that runs ``jupyter labextension install`` against a local folder
  254. path on the user's machine or a provided tarball. Any valid
  255. ``npm install`` specifier can be used in
  256. ``jupyter labextension install`` (e.g. ``foo@latest``, ``bar@3.0.0.0``,
  257. ``path/to/folder``, and ``path/to/tar.gz``).
  258. Testing your extension
  259. ^^^^^^^^^^^^^^^^^^^^^^
  260. There are a number of helper functions in ``testutils`` in this repo (which
  261. is a public ``npm`` package called ``@jupyterlab/testutils``) that can be used when
  262. writing tests for an extension. See ``tests/test-application`` for an example
  263. of the infrastructure needed to run tests. There is a ``karma`` config file
  264. that points to the parent directory's ``karma`` config, and a test runner,
  265. ``run-test.py`` that starts a Jupyter server.
  266. If you are using `jest <https://jestjs.io/>`__ to test your extension, you will
  267. need to transpile the jupyterlab packages to ``commonjs`` as they are using ES6 modules
  268. that ``node`` does not support.
  269. To transpile jupyterlab packages, you need to install the following package:
  270. ::
  271. jlpm add --dev jest@^24 @types/jest@^24 ts-jest@^24 @babel/core@^7 @babel/preset-env@^7
  272. Then in `jest.config.js`, you will specify to use babel for js files and ignore
  273. all node modules except the jupyterlab ones:
  274. ::
  275. module.exports = {
  276. preset: 'ts-jest/presets/js-with-babel',
  277. moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  278. transformIgnorePatterns: ['/node_modules/(?!(@jupyterlab/.*)/)'],
  279. globals: {
  280. 'ts-jest': {
  281. tsConfig: 'tsconfig.json'
  282. }
  283. },
  284. ... // Other options useful for your extension
  285. };
  286. Finally, you will need to configure babel with a ``babel.config.js`` file containing:
  287. ::
  288. module.exports = {
  289. presets: [
  290. [
  291. '@babel/preset-env',
  292. {
  293. targets: {
  294. node: 'current'
  295. }
  296. }
  297. ]
  298. ]
  299. };
  300. .. _rendermime:
  301. Mime Renderer Extensions
  302. ~~~~~~~~~~~~~~~~~~~~~~~~
  303. Mime Renderer extensions are a convenience for creating an extension
  304. that can render mime data and potentially render files of a given type.
  305. We provide a cookiecutter for mime renderer extensions in TypeScript
  306. `here <https://github.com/jupyterlab/mimerender-cookiecutter-ts>`__.
  307. Mime renderer extensions are more declarative than standard extensions.
  308. The extension is treated the same from the command line perspective
  309. (``jupyter labextension install`` ), but it does not directly create
  310. JupyterLab plugins. Instead it exports an interface given in the
  311. `rendermime-interfaces <https://jupyterlab.github.io/jupyterlab/interfaces/_rendermime_interfaces_src_index_.irendermime.iextension.html>`__
  312. package.
  313. The JupyterLab repo has an example mime renderer extension for
  314. `pdf <https://github.com/jupyterlab/jupyterlab/tree/master/packages/pdf-extension>`__
  315. files. It provides a mime renderer for pdf data and registers itself as
  316. a document renderer for pdf file types.
  317. The JupyterLab organization also has a mime renderer extension tutorial
  318. which adds mp4 video rendering to the application
  319. `here <https://github.com/jupyterlab/jupyterlab-mp4>`__.
  320. The ``rendermime-interfaces`` package is intended to be the only
  321. JupyterLab package needed to create a mime renderer extension (using the
  322. interfaces in TypeScript or as a form of documentation if using plain
  323. JavaScript).
  324. The only other difference from a standard extension is that has a
  325. ``jupyterlab`` key in its ``package.json`` with ``"mimeExtension"``
  326. metadata. The value can be ``true`` to use the main module of the
  327. package, or a string path to a specific module (e.g. ``"lib/foo"``).
  328. The mime renderer can update its data by calling ``.setData()`` on the
  329. model it is given to render. This can be used for example to add a
  330. ``png`` representation of a dynamic figure, which will be picked up by a
  331. notebook model and added to the notebook document. When using
  332. ``IDocumentWidgetFactoryOptions``, you can update the document model by
  333. calling ``.setData()`` with updated data for the rendered MIME type. The
  334. document can then be saved by the user in the usual manner.
  335. Themes
  336. ~~~~~~
  337. A theme is a JupyterLab extension that uses a ``ThemeManager`` and can
  338. be loaded and unloaded dynamically. The package must include all static
  339. assets that are referenced by ``url()`` in its CSS files. Local URLs can
  340. be used to reference files relative to the location of the referring sibling CSS files. For example ``url('images/foo.png')`` or
  341. ``url('../foo/bar.css')``\ can be used to refer local files in the
  342. theme. Absolute URLs (starting with a ``/``) or external URLs (e.g.
  343. ``https:``) can be used to refer to external assets. The path to the
  344. theme asset entry point is specified ``package.json`` under the ``"jupyterlab"``
  345. key as ``"themePath"``. See the `JupyterLab Light
  346. Theme <https://github.com/jupyterlab/jupyterlab/tree/master/packages/theme-light-extension>`__
  347. for an example. Ensure that the theme files are included in the
  348. ``"files"`` metadata in ``package.json``. Note that if you want to use SCSS, SASS, or LESS files,
  349. you must compile them to CSS and point JupyterLab to the CSS files.
  350. The theme extension is installed in the same way as a regular extension (see
  351. `extension authoring <#extension-authoring>`__).
  352. It is also possible to create a new theme using the
  353. `TypeScript theme cookiecutter <https://github.com/jupyterlab/theme-cookiecutter>`__.
  354. Standard (General-Purpose) Extensions
  355. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  356. JupyterLab's modular architecture is based around the idea
  357. that all extensions are on equal footing, and that they interact
  358. with each other through typed interfaces that are provided by ``Token`` objects.
  359. An extension can provide a ``Token`` to the application,
  360. which other extensions can then request for their own use.
  361. .. _tokens:
  362. Core Tokens
  363. ^^^^^^^^^^^
  364. The core packages of JupyterLab provide a set of tokens,
  365. which are listed here, along with short descriptions of when you
  366. might want to use them in your extensions.
  367. - ``@jupyterlab/application:IConnectionLost``: A token for invoking the dialog shown
  368. when JupyterLab has lost its connection to the server. Use this if, for some reason,
  369. you want to bring up the "connection lost" dialog under new circumstances.
  370. - ``@jupyterlab/application:IInfo``: A token providing metadata about the current
  371. application, including currently disabled extensions and whether dev mode is enabled.
  372. - ``@jupyterlab/application:IPaths``: A token providing information about various
  373. URLs and server paths for the current application. Use this token if you want to
  374. assemble URLs to use the JupyterLab REST API.
  375. - ``@jupyterlab/application:ILabStatus``: An interface for interacting with the application busy/dirty
  376. status. Use this if you want to set the application "busy" favicon, or to set
  377. the application "dirty" status, which asks the user for confirmation before leaving.
  378. - ``@jupyterlab/application:ILabShell``: An interface to the JupyterLab shell.
  379. The top-level application object also has a reference to the shell, but it has a restricted
  380. interface in order to be agnostic to different spins on the application.
  381. Use this to get more detailed information about currently active widgets and layout state.
  382. - ``@jupyterlab/application:ILayoutRestorer``: An interface to the application layout
  383. restoration functionality. Use this to have your activities restored across
  384. page loads.
  385. - ``@jupyterlab/application:IMimeDocumentTracker``: A widget tracker for documents
  386. rendered using a mime renderer extension. Use this if you want to list and interact
  387. with documents rendered by such extensions.
  388. - ``@jupyterlab/application:IRouter``: The URL router used by the application.
  389. Use this to add custom URL-routing for your extension (e.g., to invoke
  390. a command if the user navigates to a sub-path).
  391. - ``@jupyterlab/apputils:ICommandPalette``: An interface to the application command palette
  392. in the left panel. Use this to add commands to the palette.
  393. - ``@jupyterlab/apputils:ISplashScreen``: An interface to the splash screen for the application.
  394. Use this if you want to show the splash screen for your own purposes.
  395. - ``@jupyterlab/apputils:IThemeManager``: An interface to the theme manager for the application.
  396. Most extensions will not need to use this, as they can register a
  397. `theme extension <#themes>`__.
  398. - ``@jupyterlab/apputils:IWindowResolver``: An interface to a window resolver for the
  399. application. JupyterLab workspaces are given a name, which are determined using
  400. the window resolver. Require this if you want to use the name of the current workspace.
  401. - ``@jupyterlab/codeeditor:IEditorServices``: An interface to the text editor provider
  402. for the application. Use this to create new text editors and host them in your
  403. UI elements.
  404. - ``@jupyterlab/completer:ICompletionManager``: An interface to the completion manager
  405. for the application. Use this to allow your extension to invoke a completer.
  406. - ``@jupyterlab/console:IConsoleTracker``: A widget tracker for code consoles.
  407. Use this if you want to be able to iterate over and interact with code consoles
  408. created by the application.
  409. - ``@jupyterlab/console:IContentFactory``: A factory object that creates new code
  410. consoles. Use this if you want to create and host code consoles in your own UI elements.
  411. - ``@jupyterlab/docmanager:IDocumentManager``: An interface to the manager for all
  412. documents used by the application. Use this if you want to open and close documents,
  413. create and delete files, and otherwise interact with the file system.
  414. - ``@jupyterlab/documentsearch:ISearchProviderRegistry``: An interface for a registry of search
  415. providers for the application. Extensions can register their UI elements with this registry
  416. to provide find/replace support.
  417. - ``@jupyterlab/filebrowser:IFileBrowserFactory``: A factory object that creates file browsers.
  418. Use this if you want to create your own file browser (e.g., for a custom storage backend),
  419. or to interact with other file browsers that have been created by extensions.
  420. - ``@jupyterlab/fileeditor:IEditorTracker``: A widget tracker for file editors.
  421. Use this if you want to be able to iterate over and interact with file editors
  422. created by the application.
  423. - ``@jupyterlab/htmlviewer:IHTMLViewerTracker``: A widget tracker for rendered HTML documents.
  424. Use this if you want to be able to iterate over and interact with HTML documents
  425. viewed by the application.
  426. - ``@jupyterlab/imageviewer:IImageTracker``: A widget tracker for images.
  427. Use this if you want to be able to iterate over and interact with images
  428. viewed by the application.
  429. - ``@jupyterlab/inspector:IInspector``: An interface for adding variable inspectors to widgets.
  430. Use this to add the ability to hook into the variable inspector to your extension.
  431. - ``@jupyterlab/launcher:ILauncher``: An interface to the application activity launcher.
  432. Use this to add your extension activities to the launcher panel.
  433. - ``@jupyterlab/mainmenu:IMainMenu``: An interface to the main menu bar for the application.
  434. Use this if you want to add your own menu items.
  435. - ``@jupyterlab/markdownviewer:IMarkdownViewerTracker``: A widget tracker for markdown
  436. document viewers. Use this if you want to iterate over and interact with rendered markdown documents.
  437. - ``@jupyterlab/notebook:INotebookTools``: An interface to the ``Notebook Tools`` panel in the
  438. application left area. Use this to add your own functionality to the panel.
  439. - ``@jupyterlab/notebook:IContentFactory``: A factory object that creates new notebooks.
  440. Use this if you want to create and host notebooks in your own UI elements.
  441. - ``@jupyterlab/notebook:INotebookTracker``: A widget tracker for notebooks.
  442. Use this if you want to be able to iterate over and interact with notebooks
  443. created by the application.
  444. - ``@jupyterlab/rendermime:IRenderMimeRegistry``: An interface to the rendermime registry
  445. for the application. Use this to create renderers for various mime-types in your extension.
  446. Most extensions will not need to use this, as they can register a
  447. `mime renderer extension <#mime-renderer-extensions>`__.
  448. - ``@jupyterlab/rendermime:ILatexTypesetter``: An interface to the LaTeX typesetter for the
  449. application. Use this if you want to typeset math in your extension.
  450. - ``@jupyterlab/settingeditor:ISettingEditorTracker``: A widget tracker for setting editors.
  451. Use this if you want to be able to iterate over and interact with setting editors
  452. created by the application.
  453. - ``@jupyterlab/settingregistry:ISettingRegistry``: An interface to the JupyterLab settings system.
  454. Use this if you want to store settings for your application.
  455. See `extension settings <#extension-settings>`__ for more information.
  456. - ``@jupyterlab/statedb:IStateDB``: An interface to the JupyterLab state database.
  457. Use this if you want to store data that will persist across page loads.
  458. See `state database <#state-database>`__ for more information.
  459. - ``@jupyterlab/statusbar:IStatusBar``: An interface to the status bar on the application.
  460. Use this if you want to add new status bar items.
  461. - ``@jupyterlab/terminal:ITerminalTracker``: A widget tracker for terminals.
  462. Use this if you want to be able to iterate over and interact with terminals
  463. created by the application.
  464. - ``@jupyterlab/tooltip:ITooltipManager``: An interface to the tooltip manager for the application.
  465. Use this to allow your extension to invoke a tooltip.
  466. - ``@jupyterlab/vdom:IVDOMTracker``: A widget tracker for virtual DOM (VDOM) documents.
  467. Use this to iterate over and interact with VDOM instances created by the application.
  468. Standard Extension Example
  469. ^^^^^^^^^^^^^^^^^^^^^^^^^^
  470. For a concrete example of a standard extension, see :ref:`How to extend the Notebook plugin <extend-notebook-plugin>`.
  471. Notice that the mime renderer extensions use a limited,
  472. simplified interface to JupyterLab's extension system. Modifying the
  473. notebook plugin requires the full, general-purpose interface to the
  474. extension system.
  475. Storing Extension Data
  476. ^^^^^^^^^^^^^^^^^^^^^^
  477. In addition to the file system that is accessed by using the
  478. ``@jupyterlab/services`` package, JupyterLab exposes a plugin settings
  479. system that can be used to provide default setting values and user overrides.
  480. Extension Settings
  481. ``````````````````
  482. An extension can specify user settings using a JSON Schema. The schema
  483. definition should be in a file that resides in the ``schemaDir``
  484. directory that is specified in the ``package.json`` file of the
  485. extension. The actual file name should use is the part that follows the
  486. package name of extension. So for example, the JupyterLab
  487. ``apputils-extension`` package hosts several plugins:
  488. - ``'@jupyterlab/apputils-extension:menu'``
  489. - ``'@jupyterlab/apputils-extension:palette'``
  490. - ``'@jupyterlab/apputils-extension:settings'``
  491. - ``'@jupyterlab/apputils-extension:themes'``
  492. And in the ``package.json`` for ``@jupyterlab/apputils-extension``, the
  493. ``schemaDir`` field is a directory called ``schema``. Since the
  494. ``themes`` plugin requires a JSON schema, its schema file location is:
  495. ``schema/themes.json``. The plugin's name is used to automatically
  496. associate it with its settings file, so this naming convention is
  497. important. Ensure that the schema files are included in the ``"files"``
  498. metadata in ``package.json``.
  499. See the
  500. `fileeditor-extension <https://github.com/jupyterlab/jupyterlab/tree/master/packages/fileeditor-extension>`__
  501. for another example of an extension that uses settings.
  502. Note: You can override default values of the extension settings by
  503. defining new default values in an ``overrides.json`` file in the
  504. application settings directory. So for example, if you would like
  505. to set the dark theme by default instead of the light one, an
  506. ``overrides.json`` file containing the following lines needs to be
  507. added in the application settings directory (by default this is the
  508. ``share/jupyter/lab/settings`` folder).
  509. .. code:: json
  510. {
  511. "@jupyterlab/apputils-extension:themes": {
  512. "theme": "JupyterLab Dark"
  513. }
  514. }
  515. State Database
  516. ``````````````
  517. The state database can be accessed by importing ``IStateDB`` from
  518. ``@jupyterlab/statedb`` and adding it to the list of ``requires`` for
  519. a plugin:
  520. .. code:: typescript
  521. const id = 'foo-extension:IFoo';
  522. const IFoo = new Token<IFoo>(id);
  523. interface IFoo {}
  524. class Foo implements IFoo {}
  525. const plugin: JupyterFrontEndPlugin<IFoo> = {
  526. id,
  527. requires: [IStateDB],
  528. provides: IFoo,
  529. activate: (app: JupyterFrontEnd, state: IStateDB): IFoo => {
  530. const foo = new Foo();
  531. const key = `${id}:some-attribute`;
  532. // Load the saved plugin state and apply it once the app
  533. // has finished restoring its former layout.
  534. Promise.all([state.fetch(key), app.restored])
  535. .then(([saved]) => { /* Update `foo` with `saved`. */ });
  536. // Fulfill the plugin contract by returning an `IFoo`.
  537. return foo;
  538. },
  539. autoStart: true
  540. };
  541. Context Menus
  542. ^^^^^^^^^^^^^
  543. JupyterLab has an application-wide context menu available as
  544. ``app.contextMenu``. See the Lumino
  545. `docs <https://jupyterlab.github.io/lumino/widgets/interfaces/contextmenu.iitemoptions.html>`__
  546. for the item creation options. If you wish to preempt the
  547. application context menu, you can use a 'contextmenu' event listener and
  548. call ``event.stopPropagation`` to prevent the application context menu
  549. handler from being called (it is listening in the bubble phase on the
  550. ``document``). At this point you could show your own Lumino
  551. `contextMenu <https://jupyterlab.github.io/lumino/widgets/classes/contextmenu.html>`__,
  552. or simply stop propagation and let the system context menu be shown.
  553. This would look something like the following in a ``Widget`` subclass:
  554. .. code:: javascript
  555. // In `onAfterAttach()`
  556. this.node.addEventListener('contextmenu', this);
  557. // In `handleEvent()`
  558. case 'contextmenu':
  559. event.stopPropagation();
  560. .. |dependencies| image:: dependency-graph.svg
  561. Using React
  562. ^^^^^^^^^^^
  563. We also provide support for using :ref:`react` in your JupyterLab
  564. extensions, as well as in the core codebase.
  565. .. _ext-author-companion-packages:
  566. Companion Packages
  567. ^^^^^^^^^^^^^^^^^^
  568. If your extensions depends on the presence of one or more packages in the
  569. kernel, or on a notebook server extension, you can add metadata to indicate
  570. this to the extension manager by adding metadata to your package.json file.
  571. The full options available are::
  572. "jupyterlab": {
  573. "discovery": {
  574. "kernel": [
  575. {
  576. "kernel_spec": {
  577. "language": "<regexp for matching kernel language>",
  578. "display_name": "<regexp for matching kernel display name>" // optional
  579. },
  580. "base": {
  581. "name": "<the name of the kernel package>"
  582. },
  583. "overrides": { // optional
  584. "<manager name, e.g. 'pip'>": {
  585. "name": "<name of kernel package on pip, if it differs from base name>"
  586. }
  587. },
  588. "managers": [ // list of package managers that have your kernel package
  589. "pip",
  590. "conda"
  591. ]
  592. }
  593. ],
  594. "server": {
  595. "base": {
  596. "name": "<the name of the server extension package>"
  597. },
  598. "overrides": { // optional
  599. "<manager name, e.g. 'pip'>": {
  600. "name": "<name of server extension package on pip, if it differs from base name>"
  601. }
  602. },
  603. "managers": [ // list of package managers that have your server extension package
  604. "pip",
  605. "conda"
  606. ]
  607. }
  608. }
  609. }
  610. A typical setup for e.g. a jupyter-widget based package will then be::
  611. "keywords": [
  612. "jupyterlab-extension",
  613. "jupyter",
  614. "widgets",
  615. "jupyterlab"
  616. ],
  617. "jupyterlab": {
  618. "extension": true,
  619. "discovery": {
  620. "kernel": [
  621. {
  622. "kernel_spec": {
  623. "language": "^python",
  624. },
  625. "base": {
  626. "name": "myipywidgetspackage"
  627. },
  628. "managers": [
  629. "pip",
  630. "conda"
  631. ]
  632. }
  633. ]
  634. }
  635. }
  636. Currently supported package managers are:
  637. - ``pip``
  638. - ``conda``
  639. Shipping Packages
  640. ^^^^^^^^^^^^^^^^^
  641. Most extensions are single JavaScript packages, and can be shipped on npmjs.org.
  642. This makes them discoverable by the JupyterLab extension manager, provided they
  643. have the ``jupyterlab-extension`` keyword in their ``package.json``. If the package also
  644. contains a server extension (Python package), the author has two options.
  645. The server extension and the JupyterLab extension can be shipped in a single package,
  646. or they can be shipped separately.
  647. The JupyterLab extension can be bundled in a package on PyPI and conda-forge so
  648. that it ends up in the user's application directory. Note that the user will still have to run ``jupyter lab build``
  649. (or build when prompted in the UI) in order to use the extension.
  650. The general idea is to pack the Jupyterlab extension using ``npm pack``, and then
  651. use the ``data_files`` logic in ``setup.py`` to ensure the file ends up in the
  652. ``<jupyterlab_application>/share/jupyter/lab/extensions``
  653. directory.
  654. Note that even if the JupyterLab extension is unusable without the
  655. server extension, as long as you use the companion package metadata it is still
  656. useful to publish it to npmjs.org so it is discoverable by the JupyterLab extension manager.
  657. The server extension can be enabled on install by using ``data_files``.
  658. an example of this approach is `jupyterlab-matplotlib <https://github.com/matplotlib/jupyter-matplotlib/tree/ce9cc91e52065d33e57c3265282640f2aa44e08f>`__. The file used to enable the server extension is `here <https://github.com/matplotlib/jupyter-matplotlib/blob/ce9cc91e52065d33e57c3265282640f2aa44e08f/jupyter-matplotlib.json>`__. The logic to ship the JS tarball and server extension
  659. enabler is in `setup.py <https://github.com/matplotlib/jupyter-matplotlib/blob/ce9cc91e52065d33e57c3265282640f2aa44e08f/setup.py>`__. Note that the ``setup.py``
  660. file has additional logic to automatically create the JS tarball as part of the
  661. release process, but this could also be done manually.
  662. Technically, a package that contains only a JupyterLab extension could be created
  663. and published on ``conda-forge``, but it would not be discoverable by the JupyterLab
  664. extension manager.