plugin.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { ISettingRegistry } from '@jupyterlab/settingregistry';
  4. import { INotebookTracker } from '@jupyterlab/notebook';
  5. import { IMainMenu, } from '@jupyterlab/mainmenu';
  6. import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
  7. import { ILoggerRegistry } from '@jupyterlab/logconsole';
  8. import { toArray, filter } from '@lumino/algorithm';
  9. import { DisposableDelegate } from '@lumino/disposable';
  10. import { AttachedProperty } from '@lumino/properties';
  11. import { WidgetRenderer } from './renderer';
  12. import { WidgetManager, WIDGET_VIEW_MIMETYPE } from './manager';
  13. import { OutputModel, OutputView, OUTPUT_WIDGET_VERSION } from './output';
  14. import * as base from '@jupyter-widgets/base';
  15. // We import only the version from the specific module in controls so that the
  16. // controls code can be split and dynamically loaded in webpack.
  17. import { JUPYTER_CONTROLS_VERSION } from '@jupyter-widgets/controls/lib/version';
  18. import '@jupyter-widgets/base/css/index.css';
  19. import '@jupyter-widgets/controls/css/widgets-base.css';
  20. import { KernelMessage } from '@jupyterlab/services';
  21. const WIDGET_REGISTRY = [];
  22. /**
  23. * The cached settings.
  24. */
  25. const SETTINGS = { saveState: false };
  26. /**
  27. * Iterate through all widget renderers in a notebook.
  28. */
  29. function* widgetRenderers(nb) {
  30. for (let cell of nb.widgets) {
  31. if (cell.model.type === 'code') {
  32. for (let codecell of cell.outputArea.widgets) {
  33. for (let output of toArray(codecell.children())) {
  34. if (output instanceof WidgetRenderer) {
  35. yield output;
  36. }
  37. }
  38. }
  39. }
  40. }
  41. }
  42. /**
  43. * Iterate through all matching linked output views
  44. */
  45. function* outputViews(app, path) {
  46. let linkedViews = filter(app.shell.widgets(), w => w.id.startsWith('LinkedOutputView-') && w.path === path);
  47. for (let view of toArray(linkedViews)) {
  48. for (let outputs of toArray(view.children())) {
  49. for (let output of toArray(outputs.children())) {
  50. if (output instanceof WidgetRenderer) {
  51. yield output;
  52. }
  53. }
  54. }
  55. }
  56. }
  57. function* chain(...args) {
  58. for (let it of args) {
  59. yield* it;
  60. }
  61. }
  62. export function registerWidgetManager(context, rendermime, renderers) {
  63. let wManager = Private.widgetManagerProperty.get(context);
  64. if (!wManager) {
  65. wManager = new WidgetManager(context, rendermime, SETTINGS);
  66. WIDGET_REGISTRY.forEach(data => wManager.register(data));
  67. Private.widgetManagerProperty.set(context, wManager);
  68. }
  69. for (let r of renderers) {
  70. r.manager = wManager;
  71. }
  72. // Replace the placeholder widget renderer with one bound to this widget
  73. // manager.
  74. rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE);
  75. rendermime.addFactory({
  76. safe: false,
  77. mimeTypes: [WIDGET_VIEW_MIMETYPE],
  78. createRenderer: (options) => new WidgetRenderer(options, wManager)
  79. }, 0);
  80. return new DisposableDelegate(() => {
  81. if (rendermime) {
  82. rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE);
  83. }
  84. wManager.dispose();
  85. });
  86. }
  87. /**
  88. * The widget manager provider.
  89. */
  90. const plugin = {
  91. id: '@jupyter-widgets/jupyterlab-manager:plugin',
  92. requires: [IRenderMimeRegistry, ISettingRegistry],
  93. optional: [INotebookTracker, IMainMenu, ILoggerRegistry],
  94. provides: base.IJupyterWidgetRegistry,
  95. activate: activateWidgetExtension,
  96. autoStart: true
  97. };
  98. export default plugin;
  99. function updateSettings(settings) {
  100. SETTINGS.saveState = settings.get('saveState').composite;
  101. }
  102. /**
  103. * Activate the widget extension.
  104. */
  105. function activateWidgetExtension(app, rendermime, settingRegistry, tracker, menu, loggerRegistry) {
  106. const { commands } = app;
  107. const bindUnhandledIOPubMessageSignal = (nb) => {
  108. if (!loggerRegistry) {
  109. return;
  110. }
  111. const wManager = Private.widgetManagerProperty.get(nb.context);
  112. if (wManager) {
  113. wManager.onUnhandledIOPubMessage.connect((sender, msg) => {
  114. const logger = loggerRegistry.getLogger(nb.context.path);
  115. let level = 'warning';
  116. if (KernelMessage.isErrorMsg(msg) ||
  117. (KernelMessage.isStreamMsg(msg) && msg.content.name === 'stderr')) {
  118. level = 'error';
  119. }
  120. const data = Object.assign(Object.assign({}, msg.content), { output_type: msg.header.msg_type });
  121. logger.rendermime = nb.content.rendermime;
  122. logger.log({ type: 'output', data, level });
  123. });
  124. }
  125. };
  126. settingRegistry.load(plugin.id).then((settings) => {
  127. settings.changed.connect(updateSettings);
  128. updateSettings(settings);
  129. }).catch((reason) => {
  130. console.error(reason.message);
  131. });
  132. // Add a placeholder widget renderer.
  133. rendermime.addFactory({
  134. safe: false,
  135. mimeTypes: [WIDGET_VIEW_MIMETYPE],
  136. createRenderer: options => new WidgetRenderer(options)
  137. }, 0);
  138. if (tracker) {
  139. tracker.forEach(panel => {
  140. registerWidgetManager(panel.context, panel.content.rendermime, chain(widgetRenderers(panel.content), outputViews(app, panel.context.path)));
  141. bindUnhandledIOPubMessageSignal(panel);
  142. });
  143. tracker.widgetAdded.connect((sender, panel) => {
  144. registerWidgetManager(panel.context, panel.content.rendermime, chain(widgetRenderers(panel.content), outputViews(app, panel.context.path)));
  145. bindUnhandledIOPubMessageSignal(panel);
  146. });
  147. }
  148. // Add a command for creating a new Markdown file.
  149. commands.addCommand('@jupyter-widgets/jupyterlab-manager:saveWidgetState', {
  150. label: 'Save Widget State Automatically',
  151. execute: args => {
  152. return settingRegistry
  153. .set(plugin.id, 'saveState', !SETTINGS.saveState)
  154. .catch((reason) => {
  155. console.error(`Failed to set ${plugin.id}: ${reason.message}`);
  156. });
  157. },
  158. isToggled: () => SETTINGS.saveState
  159. });
  160. if (menu) {
  161. menu.settingsMenu.addGroup([
  162. { command: '@jupyter-widgets/jupyterlab-manager:saveWidgetState' }
  163. ]);
  164. }
  165. WIDGET_REGISTRY.push({
  166. name: '@jupyter-widgets/base',
  167. version: base.JUPYTER_WIDGETS_VERSION,
  168. exports: {
  169. WidgetModel: base.WidgetModel,
  170. WidgetView: base.WidgetView,
  171. DOMWidgetView: base.DOMWidgetView,
  172. DOMWidgetModel: base.DOMWidgetModel,
  173. LayoutModel: base.LayoutModel,
  174. LayoutView: base.LayoutView,
  175. StyleModel: base.StyleModel,
  176. StyleView: base.StyleView
  177. }
  178. });
  179. WIDGET_REGISTRY.push({
  180. name: '@jupyter-widgets/controls',
  181. version: JUPYTER_CONTROLS_VERSION,
  182. exports: () => {
  183. return new Promise((resolve, reject) => {
  184. require.ensure(['@jupyter-widgets/controls'], (require) => {
  185. resolve(require('@jupyter-widgets/controls'));
  186. }, (err) => {
  187. reject(err);
  188. }, '@jupyter-widgets/controls');
  189. });
  190. }
  191. });
  192. WIDGET_REGISTRY.push({
  193. name: '@jupyter-widgets/output',
  194. version: OUTPUT_WIDGET_VERSION,
  195. exports: { OutputModel, OutputView }
  196. });
  197. return {
  198. registerWidget(data) {
  199. WIDGET_REGISTRY.push(data);
  200. }
  201. };
  202. }
  203. var Private;
  204. (function (Private) {
  205. /**
  206. * A private attached property for a widget manager.
  207. */
  208. Private.widgetManagerProperty = new AttachedProperty({
  209. name: 'widgetManager',
  210. create: () => undefined
  211. });
  212. })(Private || (Private = {}));