瀏覽代碼

Make document contexts take a model type and refactoring

Steven Silvester 9 年之前
父節點
當前提交
4459d9c794

+ 9 - 9
src/docmanager/context.ts

@@ -30,7 +30,7 @@ import {
 /**
  * An implementation of a document context.
  */
-class Context implements IDocumentContext {
+class Context implements IDocumentContext<IDocumentModel> {
   /**
    * Construct a new document context.
    */
@@ -42,21 +42,21 @@ class Context implements IDocumentContext {
   /**
    * A signal emitted when the kernel changes.
    */
-  get kernelChanged(): ISignal<IDocumentContext, IKernel> {
+  get kernelChanged(): ISignal<Context, IKernel> {
     return Private.kernelChangedSignal.bind(this);
   }
 
   /**
    * A signal emitted when the path changes.
    */
-  get pathChanged(): ISignal<IDocumentContext, string> {
+  get pathChanged(): ISignal<Context, string> {
     return Private.pathChangedSignal.bind(this);
   }
 
   /**
    * A signal emitted when the model is saved or reverted.
    */
-  get dirtyCleared(): ISignal<IDocumentContext, void> {
+  get dirtyCleared(): ISignal<Context, void> {
     return Private.dirtyClearedSignal.bind(this);
   }
 
@@ -285,7 +285,7 @@ class ContextManager implements IDisposable {
   /**
    * Get a context by id.
    */
-  getContext(id: string): IDocumentContext {
+  getContext(id: string): IDocumentContext<IDocumentModel> {
     return this._contexts[id].context;
   }
 
@@ -531,7 +531,7 @@ namespace Private {
    */
   export
   interface IContextEx {
-    context: IDocumentContext;
+    context: IDocumentContext<IDocumentModel>;
     model: IDocumentModel;
     session: INotebookSession;
     opts: IContentsOpts;
@@ -544,17 +544,17 @@ namespace Private {
    * A signal emitted when the kernel changes.
    */
   export
-  const kernelChangedSignal = new Signal<IDocumentContext, IKernel>();
+  const kernelChangedSignal = new Signal<Context, IKernel>();
 
   /**
    * A signal emitted when the path changes.
    */
   export
-  const pathChangedSignal = new Signal<IDocumentContext, string>();
+  const pathChangedSignal = new Signal<Context, string>();
 
   /**
    * A signal emitted when the model is saved or reverted.
    */
   export
-  const dirtyClearedSignal = new Signal<IDocumentContext, void>();
+  const dirtyClearedSignal = new Signal<Context, void>();
 }

+ 9 - 8
src/docmanager/manager.ts

@@ -36,7 +36,8 @@ import {
 } from '../filebrowser/browser';
 
 import {
-  DocumentRegistry, IDocumentContext, IWidgetFactory, IWidgetFactoryOptions
+  DocumentRegistry, IDocumentContext, IWidgetFactory, IWidgetFactoryOptions,
+  IDocumentModel
 } from '../docregistry';
 
 import {
@@ -312,12 +313,12 @@ class DocumentManager implements IDisposable {
     let model = this._contextManager.getModel(id);
     model.initialize();
     let context = this._contextManager.getContext(id);
-    let child = factory.createNew(model, context, kernel);
+    let child = factory.createNew(context, kernel);
     parent.setContent(child);
     // Handle widget extensions.
     let disposables = new DisposableSet();
     for (let extender of this._registry.getWidgetExtensions(parent.name)) {
-      disposables.add(extender.createNew(child, model, context));
+      disposables.add(extender.createNew(child, context));
     }
     parent.disposed.connect(() => {
       disposables.dispose();
@@ -348,7 +349,7 @@ class DocumentWrapper extends Widget {
   /**
    * Construct a new document widget.
    */
-  constructor(name: string, id: string, manager: ContextManager, factory: IWidgetFactory<Widget>, widgets: { [key: string]: DocumentWrapper[] }) {
+  constructor(name: string, id: string, manager: ContextManager, factory: IWidgetFactory<Widget, IDocumentModel>, widgets: { [key: string]: DocumentWrapper[] }) {
     super();
     this.addClass(DOCUMENT_CLASS);
     this.layout = new PanelLayout();
@@ -376,7 +377,7 @@ class DocumentWrapper extends Widget {
    * #### Notes
    * This is a read-only property.
    */
-  get context(): IDocumentContext {
+  get context(): IDocumentContext<IDocumentModel> {
     return this._manager.getContext(this._id);
   }
 
@@ -445,7 +446,7 @@ class DocumentWrapper extends Widget {
     this._maybeClose(model.dirty).then(result => {
       if (result) {
         // Let the widget factory handle closing.
-        return this._factory.beforeClose(model, this.context, child);
+        return this._factory.beforeClose(child, this.context);
       }
       return result;
     }).then(result => {
@@ -517,7 +518,7 @@ class DocumentWrapper extends Widget {
   }
 
   private _manager: ContextManager = null;
-  private _factory: IWidgetFactory<Widget> = null;
+  private _factory: IWidgetFactory<Widget, IDocumentModel> = null;
   private _id = '';
   private _name = '';
   private _widgets: { [key: string]: DocumentWrapper[] } = null;
@@ -539,6 +540,6 @@ namespace Private {
    */
   export
   interface IWidgetFactoryEx extends IWidgetFactoryOptions {
-    factory: IWidgetFactory<Widget>;
+    factory: IWidgetFactory<Widget, IDocumentModel>;
   }
 }

+ 3 - 3
src/docregistry/default.ts

@@ -264,7 +264,7 @@ class Base64ModelFactory extends TextModelFactory {
  * The default implemetation of a widget factory.
  */
 export
-abstract class ABCWidgetFactory implements IWidgetFactory<Widget> {
+abstract class ABCWidgetFactory implements IWidgetFactory<Widget, IDocumentModel> {
   /**
    * Get whether the model factory has been disposed.
    */
@@ -282,7 +282,7 @@ abstract class ABCWidgetFactory implements IWidgetFactory<Widget> {
   /**
    * Create a new widget given a document model and a context.
    */
-  abstract createNew(model: IDocumentModel, context: IDocumentContext, kernel?: IKernelId): Widget;
+  abstract createNew(context: IDocumentContext<IDocumentModel>, kernel?: IKernelId): Widget;
 
   /**
    * Take an action on a widget before closing it.
@@ -290,7 +290,7 @@ abstract class ABCWidgetFactory implements IWidgetFactory<Widget> {
    * @returns A promise that resolves to true if the document should close
    *   and false otherwise.
    */
-  beforeClose(model: IDocumentModel, context: IDocumentContext, widget: Widget): Promise<boolean> {
+  beforeClose(widget: Widget, context: IDocumentContext<IDocumentModel>): Promise<boolean> {
     // There is nothing specific to do.
     return Promise.resolve(true);
   }

+ 9 - 9
src/docregistry/interfaces.ts

@@ -104,7 +104,7 @@ interface IDocumentModel extends IDisposable {
 /**
  * The document context object.
  */
-export interface IDocumentContext extends IDisposable {
+export interface IDocumentContext<T extends IDocumentModel> extends IDisposable {
   /**
    * The unique id of the context.
    *
@@ -119,7 +119,7 @@ export interface IDocumentContext extends IDisposable {
    * #### Notes
    * This is a read-only property
    */
-  model: IDocumentModel;
+  model: T;
 
   /**
    * The current kernel associated with the document.
@@ -157,12 +157,12 @@ export interface IDocumentContext extends IDisposable {
   /**
    * A signal emitted when the kernel changes.
    */
-  kernelChanged: ISignal<IDocumentContext, IKernel>;
+  kernelChanged: ISignal<IDocumentContext<T>, IKernel>;
 
   /**
    * A signal emitted when the path changes.
    */
-  pathChanged: ISignal<IDocumentContext, string>;
+  pathChanged: ISignal<IDocumentContext<T>, string>;
 
   /**
    * Change the current kernel associated with the document.
@@ -254,11 +254,11 @@ interface IWidgetFactoryOptions {
  * The interface for a widget factory.
  */
 export
-interface IWidgetFactory<T extends Widget> extends IDisposable {
+interface IWidgetFactory<T extends Widget, U extends IDocumentModel> extends IDisposable {
   /**
    * Create a new widget.
    */
-  createNew(model: IDocumentModel, context: IDocumentContext, kernel?: IKernelId): T;
+  createNew(context: IDocumentContext<U>, kernel?: IKernelId): T;
 
   /**
    * Take an action on a widget before closing it.
@@ -266,7 +266,7 @@ interface IWidgetFactory<T extends Widget> extends IDisposable {
    * @returns A promise that resolves to true if the document should close
    *   and false otherwise.
    */
-  beforeClose(model: IDocumentModel, context: IDocumentContext, widget: Widget): Promise<boolean>;
+  beforeClose(widget: T, context: IDocumentContext<U>): Promise<boolean>;
 }
 
 
@@ -274,11 +274,11 @@ interface IWidgetFactory<T extends Widget> extends IDisposable {
  * An interface for a widget extension.
  */
 export
-interface IWidgetExtension<T extends Widget> {
+interface IWidgetExtension<T extends Widget, U extends IDocumentModel> {
   /**
    * Create a new extension for a given widget.
    */
-   createNew(widget: T, model: IDocumentModel, context: IDocumentContext): IDisposable;
+   createNew(widget: T, context: IDocumentContext<U>): IDisposable;
 }
 
 

+ 2 - 2
src/docregistry/kernelselector.ts

@@ -10,7 +10,7 @@ import {
 } from '../dialog';
 
 import {
-  IDocumentContext
+  IDocumentContext, IDocumentModel
 } from './interfaces';
 
 
@@ -95,7 +95,7 @@ function selectKernel(options: IKernelSelection): Promise<IKernelId> {
  * Change the kernel on a context.
  */
 export
-function selectKernelForContext(context: IDocumentContext, host?: HTMLElement): Promise<void> {
+function selectKernelForContext(context: IDocumentContext<IDocumentModel>, host?: HTMLElement): Promise<void> {
   return context.listSessions().then(sessions => {
     let options = {
       name: context.path.split('/').pop(),

+ 8 - 7
src/docregistry/registry.ts

@@ -14,7 +14,8 @@ import {
 
 import {
   IModelFactory, IWidgetFactory, IWidgetFactoryOptions,
-  IFileType, IKernelPreference, IFileCreator, IWidgetExtension
+  IFileType, IKernelPreference, IFileCreator, IWidgetExtension,
+  IDocumentModel
 } from './interfaces';
 
 
@@ -74,7 +75,7 @@ class DocumentRegistry implements IDisposable {
    * If a factory is already registered as a default for a given extension or
    * as the global default, this factory will override the existing default.
    */
-  addWidgetFactory(factory: IWidgetFactory<Widget>, options: IWidgetFactoryOptions): IDisposable {
+  addWidgetFactory(factory: IWidgetFactory<Widget, IDocumentModel>, options: IWidgetFactoryOptions): IDisposable {
     let name = options.displayName;
     let exOpt = utils.copy(options) as Private.IWidgetFactoryEx;
     exOpt.factory = factory;
@@ -136,7 +137,7 @@ class DocumentRegistry implements IDisposable {
    *
    * @returns A disposable which will unregister the extension.
    */
-  addWidgetExtension(widgetName: string, extension: IWidgetExtension<Widget>): IDisposable {
+  addExtension(widgetName: string, extension: IWidgetExtension<Widget, IDocumentModel>): IDisposable {
     if (!(widgetName in this._extenders)) {
       this._extenders[widgetName] = [];
     }
@@ -319,7 +320,7 @@ class DocumentRegistry implements IDisposable {
    *
    * @returns A widget factory instance.
    */
-  getWidgetFactory(widgetName: string): IWidgetFactory<Widget> {
+  getWidgetFactory(widgetName: string): IWidgetFactory<Widget, IDocumentModel> {
     return this._getWidgetFactoryEx(widgetName).factory;
   }
 
@@ -330,7 +331,7 @@ class DocumentRegistry implements IDisposable {
    *
    * @returns A new array of widget extensions.
    */
-  getWidgetExtensions(widgetName: string): IWidgetExtension<Widget>[] {
+  getWidgetExtensions(widgetName: string): IWidgetExtension<Widget, IDocumentModel>[] {
     if (!(widgetName in this._extenders)) {
       return [];
     }
@@ -356,7 +357,7 @@ class DocumentRegistry implements IDisposable {
   private _defaultWidgetFactories: { [key: string]: string } = Object.create(null);
   private _fileTypes: IFileType[] = [];
   private _creators: IFileCreator[] = [];
-  private _extenders: { [key: string] : IWidgetExtension<Widget>[] } = Object.create(null);
+  private _extenders: { [key: string] : IWidgetExtension<Widget, IDocumentModel>[] } = Object.create(null);
 }
 
 
@@ -369,6 +370,6 @@ namespace Private {
    */
   export
   interface IWidgetFactoryEx extends IWidgetFactoryOptions {
-    factory: IWidgetFactory<Widget>;
+    factory: IWidgetFactory<Widget, IDocumentModel>;
   }
 }

+ 6 - 5
src/editorwidget/widget.ts

@@ -42,10 +42,11 @@ class EditorWidget extends CodeMirrorWidget {
   /**
    * Construct a new editor widget.
    */
-  constructor(model: IDocumentModel, context: IDocumentContext) {
+  constructor(context: IDocumentContext<IDocumentModel>) {
     super();
     this.addClass(EDITOR_CLASS);
     let editor = this.editor;
+    let model = context.model;
     editor.setOption('lineNumbers', true);
     let doc = editor.getDoc();
     doc.setValue(model.toString());
@@ -84,14 +85,14 @@ class EditorWidget extends CodeMirrorWidget {
  * A widget factory for editors.
  */
 export
-class EditorWidgetFactory extends ABCWidgetFactory implements IWidgetFactory<EditorWidget> {
+class EditorWidgetFactory extends ABCWidgetFactory implements IWidgetFactory<EditorWidget, IDocumentModel> {
   /**
-   * Create a new widget given a document model and a context.
+   * Create a new widget given a context.
    */
-  createNew(model: IDocumentModel, context: IDocumentContext, kernel?: IKernelId): EditorWidget {
+  createNew(context: IDocumentContext<IDocumentModel>, kernel?: IKernelId): EditorWidget {
     if (kernel) {
       context.changeKernel(kernel);
     }
-    return new EditorWidget(model, context);
+    return new EditorWidget(context);
   }
 }

+ 11 - 14
src/imagewidget/widget.ts

@@ -33,20 +33,19 @@ class ImageWidget extends Widget {
   /**
    * Construct a new image widget.
    */
-  constructor(model: IDocumentModel, context: IDocumentContext) {
+  constructor(context: IDocumentContext<IDocumentModel>) {
     super();
-    this._model = model;
     this._context = context;
     this.node.tabIndex = -1;
     this.node.style.overflowX = 'auto';
     this.node.style.overflowY = 'auto';
-    if (model.toString()) {
+    if (context.model.toString()) {
       this.update();
     }
     context.pathChanged.connect(() => {
       this.update();
     });
-    model.contentChanged.connect(() => {
+    context.model.contentChanged.connect(() => {
       this.update();
     });
   }
@@ -58,7 +57,6 @@ class ImageWidget extends Widget {
     if (this.isDisposed) {
       return;
     }
-    this._model = null;
     this._context = null;
     super.dispose();
   }
@@ -69,13 +67,12 @@ class ImageWidget extends Widget {
   protected onUpdateRequest(msg: Message): void {
     this.title.text = this._context.path.split('/').pop();
     let node = this.node as HTMLImageElement;
-    let content = this._model.toString();
-    let model = this._context.contentsModel;
-    node.src = `data:${model.mimetype};${model.format},${content}`;
+    let content = this._context.toString();
+    let cm = this._context.contentsModel;
+    node.src = `data:${cm.mimetype};${cm.format},${content}`;
   }
 
-  private _model: IDocumentModel;
-  private _context: IDocumentContext;
+  private _context: IDocumentContext<IDocumentModel>;
 }
 
 
@@ -83,11 +80,11 @@ class ImageWidget extends Widget {
  * A widget factory for images.
  */
 export
-class ImageWidgetFactory extends ABCWidgetFactory implements IWidgetFactory<ImageWidget> {
+class ImageWidgetFactory extends ABCWidgetFactory implements IWidgetFactory<ImageWidget, IDocumentModel> {
   /**
-   * Create a new widget given a document model and a context.
+   * Create a new widget given a context.
    */
-  createNew(model: IDocumentModel, context: IDocumentContext, kernel?: IKernelId): ImageWidget {
-    return new ImageWidget(model, context);
+  createNew(context: IDocumentContext<IDocumentModel>, kernel?: IKernelId): ImageWidget {
+    return new ImageWidget(context);
   }
 }

+ 30 - 45
src/notebook/notebook/default-toolbar.ts

@@ -5,10 +5,6 @@ import {
   IKernel, KernelStatus
 } from 'jupyter-js-services';
 
-import {
-  IDocumentContext
-} from '../../docregistry';
-
 import {
   Widget
 } from 'phosphor-widget';
@@ -17,6 +13,10 @@ import {
   NotebookPanel
 } from './panel';
 
+import {
+  Notebook
+} from './widget';
+
 import {
   ToolbarButton
 } from './toolbar';
@@ -171,7 +171,7 @@ namespace ToolbarItems {
     return new ToolbarButton({
       className: TOOLBAR_RUN,
       onClick: () => {
-        NotebookActions.runAndAdvance(panel.content, panel.context.kernel);
+        NotebookActions.runAndAdvance(panel.content, panel.kernel);
       },
       tooltip: 'Run the selected cell(s) and advance'
     });
@@ -185,7 +185,7 @@ namespace ToolbarItems {
     return new ToolbarButton({
       className: TOOLBAR_INTERRUPT,
       onClick: () => {
-        if (panel.context.kernel) {
+        if (panel.kernel) {
           panel.context.kernel.interrupt();
         }
       },
@@ -212,7 +212,7 @@ namespace ToolbarItems {
    */
   export
   function createCellTypeItem(panel: NotebookPanel): Widget {
-    return new CellTypeSwitcher(panel);
+    return new CellTypeSwitcher(panel.content);
   }
 
   /**
@@ -223,13 +223,13 @@ namespace ToolbarItems {
     let widget = new Widget();
     widget.addClass(TOOLBAR_KERNEL);
     widget.node.textContent = 'No Kernel!';
-    if (panel.context.kernel) {
-      panel.context.kernel.getKernelSpec().then(spec => {
+    if (panel.kernel) {
+      panel.kernel.getKernelSpec().then(spec => {
         widget.node.textContent = spec.display_name;
       });
     }
-    panel.context.kernelChanged.connect(() => {
-      panel.context.kernel.getKernelSpec().then(spec => {
+    panel.kernelChanged.connect(() => {
+      panel.kernel.getKernelSpec().then(spec => {
         widget.node.textContent = spec.display_name;
       });
     });
@@ -241,7 +241,7 @@ namespace ToolbarItems {
    */
   export
   function createKernelStatusItem(panel: NotebookPanel): Widget {
-    return new KernelIndicator(panel.context);
+    return new KernelIndicator(panel);
   }
 
   /**
@@ -289,7 +289,7 @@ class CellTypeSwitcher extends Widget {
   /**
    * Construct a new cell type switcher.
    */
-  constructor(panel: NotebookPanel) {
+  constructor(content: Notebook) {
     super();
     this.addClass(TOOLBAR_CELLTYPE);
 
@@ -297,39 +297,24 @@ class CellTypeSwitcher extends Widget {
     // Change current cell type on a change in the dropdown.
     select.addEventListener('change', event => {
       if (!this._changeGuard) {
-        NotebookActions.changeCellType(panel.content, select.value);
-      }
-    });
-    // Follow the type of the current cell.
-    panel.content.stateChanged.connect((sender, args) => {
-      if (!panel.model) {
-        return;
-      }
-      if (args.name === 'activeCellIndex') {
-        this._changeGuard = true;
-        select.value = panel.model.cells.get(args.newValue).type;
-        this._changeGuard = false;
+        NotebookActions.changeCellType(content, select.value);
       }
     });
 
-    panel.content.modelChanged.connect(() => {
-      this.followModel(panel);
-    });
-    if (panel.model) {
-      this.followModel(panel);
+    // Set the initial value.
+    let index = content.activeCellIndex;
+    if (content.model) {
+      select.value = content.model.cells.get(index).type;
     }
-  }
 
-  followModel(panel: NotebookPanel): void {
-    let select = this.node.firstChild as HTMLSelectElement;
-    // Set the initial value.
-    let index = panel.content.activeCellIndex;
-    select.value = panel.model.cells.get(index).type;
-    // Follow a change in the cells.
-    panel.content.model.cells.changed.connect((sender, args) => {
-      index = panel.content.activeCellIndex;
+    // Follow the type of the current cell.
+    content.stateChanged.connect(() => {
+      if (!content.model) {
+        return;
+      }
+      index = content.activeCellIndex;
       this._changeGuard = true;
-      select.value = panel.model.cells.get(index).type;
+      select.value = content.model.cells.get(index).type;
       this._changeGuard = false;
     });
   }
@@ -345,17 +330,17 @@ class KernelIndicator extends Widget {
   /**
    * Construct a new kernel status widget.
    */
-  constructor(context: IDocumentContext) {
+  constructor(panel: NotebookPanel) {
     super();
     this.addClass(TOOLBAR_INDICATOR);
-    if (context.kernel) {
-      this._handleStatus(context.kernel, context.kernel.status);
-      context.kernel.statusChanged.connect(this._handleStatus, this);
+    if (panel.kernel) {
+      this._handleStatus(panel.kernel, panel.kernel.status);
+      panel.kernel.statusChanged.connect(this._handleStatus, this);
     } else {
       this.addClass(TOOLBAR_BUSY);
       this.node.title = 'No Kernel!';
     }
-    context.kernelChanged.connect((c, kernel) => {
+    panel.kernelChanged.connect((c, kernel) => {
       this._handleStatus(kernel, kernel.status);
       kernel.statusChanged.connect(this._handleStatus, this);
     });

+ 33 - 10
src/notebook/notebook/panel.ts

@@ -129,6 +129,13 @@ class NotebookPanel extends Widget {
     return Private.contextChangedSignal.bind(this);
   }
 
+  /**
+   * A signal emitted when the kernel used by the panel changes.
+   */
+  get kernelChanged(): ISignal<NotebookPanel, IKernel> {
+    return Private.kernelChangedSignal.bind(this);
+  }
+
   /**
    * Get the toolbar used by the widget.
    */
@@ -146,6 +153,16 @@ class NotebookPanel extends Widget {
     return this._content;
   }
 
+  /**
+   * Get the current kernel used by the panel.
+   *
+   * #### Notes
+   * This is a a read-only property.
+   */
+  get kernel(): IKernel {
+    return this._context ? this._context.kernel : null;
+  }
+
   /**
    * Get the rendermime instance used by the widget.
    *
@@ -174,11 +191,10 @@ class NotebookPanel extends Widget {
   }
 
   /**
-   * Get the model used by the widget.
+   * The model for the widget.
    *
    * #### Notes
-   * This is a read-only property.  Changing the model on the `content`
-   * directly would result in undefined behavior.
+   * This is a read-only property.
    */
   get model(): INotebookModel {
     return this._content.model;
@@ -191,10 +207,10 @@ class NotebookPanel extends Widget {
    * Changing the context also changes the model on the
    * `content`.
    */
-  get context(): IDocumentContext {
+  get context(): IDocumentContext<INotebookModel> {
     return this._context;
   }
-  set context(newValue: IDocumentContext) {
+  set context(newValue: IDocumentContext<INotebookModel>) {
     newValue = newValue || null;
     if (newValue === this._context) {
       return;
@@ -256,15 +272,16 @@ class NotebookPanel extends Widget {
    * #### Notes
    * The default implementation is a no-op.
    */
-  protected onContextChanged(oldValue: IDocumentContext, newValue: IDocumentContext): void { }
+  protected onContextChanged(oldValue: IDocumentContext<INotebookModel>, newValue: IDocumentContext<INotebookModel>): void { }
 
   /**
    * Handle a change in the kernel by updating the document metadata.
    */
-  protected onKernelChanged(context: IDocumentContext, kernel: IKernel): void {
+  protected onKernelChanged(context: IDocumentContext<INotebookModel>, kernel: IKernel): void {
     if (!this.model) {
       return;
     }
+    this.kernelChanged.emit(kernel);
     kernel.kernelInfo().then(info => {
       let infoCursor = this.model.getMetadata('language_info');
       infoCursor.setValue(info.language_info);
@@ -295,7 +312,7 @@ class NotebookPanel extends Widget {
   /**
    * Handle a change to the document path.
    */
-  protected onPathChanged(sender: IDocumentContext, path: string): void {
+  protected onPathChanged(sender: IDocumentContext<INotebookModel>, path: string): void {
     this.title.text = path.split('/').pop();
   }
 
@@ -401,7 +418,7 @@ class NotebookPanel extends Widget {
   /**
    * Handle a change in the context.
    */
-  private _onContextChanged(oldValue: IDocumentContext, newValue: IDocumentContext): void {
+  private _onContextChanged(oldValue: IDocumentContext<INotebookModel>, newValue: IDocumentContext<INotebookModel>): void {
     if (oldValue) {
       oldValue.kernelChanged.disconnect(this.onKernelChanged, this);
       oldValue.pathChanged.disconnect(this.onPathChanged, this);
@@ -425,7 +442,7 @@ class NotebookPanel extends Widget {
   }
 
   private _rendermime: RenderMime<Widget> = null;
-  private _context: IDocumentContext = null;
+  private _context: IDocumentContext<INotebookModel> = null;
   private _content: Notebook = null;
   private _toolbar: NotebookToolbar = null;
   private _clipboard: IClipboard = null;
@@ -528,4 +545,10 @@ namespace Private {
    */
   export
   const contextChangedSignal = new Signal<NotebookPanel, void>();
+
+  /**
+   * A signal emitted when the kernel used by the panel changes.
+   */
+  export
+  const kernelChangedSignal = new Signal<NotebookPanel, IKernel>();
 }

+ 4 - 3
src/notebook/notebook/widgetfactory.ts

@@ -38,7 +38,7 @@ import {
  * A widget factory for notebook panels.
  */
 export
-class NotebookWidgetFactory implements IWidgetFactory<NotebookPanel> {
+class NotebookWidgetFactory implements IWidgetFactory<NotebookPanel, INotebookModel> {
   /**
    * Construct a new notebook widget factory.
    *
@@ -76,8 +76,9 @@ class NotebookWidgetFactory implements IWidgetFactory<NotebookPanel> {
    * The factory will start the appropriate kernel and populate
    * the default toolbar items using `ToolbarItems.populateDefaults`.
    */
-  createNew(model: INotebookModel, context: IDocumentContext, kernel?: IKernelId): NotebookPanel {
+  createNew(context: IDocumentContext<INotebookModel>, kernel?: IKernelId): NotebookPanel {
     let rendermime = this._rendermime.clone();
+    let model = context.model;
     if (kernel) {
       context.changeKernel(kernel);
     } else {
@@ -98,7 +99,7 @@ class NotebookWidgetFactory implements IWidgetFactory<NotebookPanel> {
    *
    * ### The default implementation is a no-op.
    */
-  beforeClose(model: INotebookModel, context: IDocumentContext, widget: NotebookPanel): Promise<boolean> {
+  beforeClose(widget: NotebookPanel, context: IDocumentContext<INotebookModel>): Promise<boolean> {
     // No special action required.
     return Promise.resolve(true);
   }

+ 2 - 2
src/notebook/plugin.ts

@@ -173,8 +173,8 @@ class TrackingNotebookWidgetFactory extends NotebookWidgetFactory {
   /**
    * Create a new widget.
    */
-  createNew(model: INotebookModel, context: IDocumentContext, kernel?: IKernelId): NotebookPanel {
-    let widget = super.createNew(model, context, kernel);
+  createNew(context: IDocumentContext<INotebookModel>, kernel?: IKernelId): NotebookPanel {
+    let widget = super.createNew(context, kernel);
     Private.notebookTracker.activeNotebook = widget;
     return widget;
   }

+ 6 - 6
src/widgets/index.ts

@@ -28,7 +28,7 @@ import {
 } from '../rendermime';
 
 import {
-  IDocumentContext
+  IDocumentContext, IDocumentModel
 } from '../docregistry';
 
 import 'jquery-ui/themes/smoothness/jquery-ui.min.css';
@@ -69,8 +69,8 @@ class BackboneViewWrapper extends Widget {
  */
 export
 class WidgetManager extends ManagerBase<Widget> implements IDisposable {
-  constructor(context: IDocumentContext) {
-    super()
+  constructor(context: IDocumentContext<IDocumentModel>) {
+    super();
     this._context = context;
 
     let newKernel = (kernel: IKernel) => {
@@ -79,12 +79,12 @@ class WidgetManager extends ManagerBase<Widget> implements IDisposable {
         }
         this._commRegistration = kernel.registerCommTarget(this.comm_target_name,
         (comm, msg) => {this.handle_comm_open(comm, msg)});
-    }
+    };
 
     context.kernelChanged.connect((sender, kernel) => {
       this.validateVersion();
       newKernel(kernel);
-    })
+    });
 
     if (context.kernel) {
       this.validateVersion();
@@ -150,7 +150,7 @@ class WidgetManager extends ManagerBase<Widget> implements IDisposable {
     this._context = null;
   }
 
-  _context: IDocumentContext;
+  _context: IDocumentContext<IDocumentModel>;
   _commRegistration: IDisposable;
 }
 

+ 8 - 5
src/widgets/plugin.ts

@@ -13,6 +13,10 @@ import {
   Widget
 } from 'phosphor-widget';
 
+import {
+  INotebookModel
+} from '../notebook/notebook/model';
+
 import {
   NotebookPanel
 } from '../notebook/notebook/panel';
@@ -43,21 +47,20 @@ const widgetManagerExtension = {
 };
 
 export
-class IPyWidgetExtension implements IWidgetExtension<NotebookPanel>{
+class IPyWidgetExtension implements IWidgetExtension<NotebookPanel, INotebookModel> {
   /**
    * Create a new extension object.
    */
-  createNew(nb: NotebookPanel, model: IDocumentModel,
-            context: IDocumentContext): IDisposable {
+  createNew(nb: NotebookPanel, context: IDocumentContext<INotebookModel>): IDisposable {
     let wManager = new WidgetManager(context);
     let wRenderer = new WidgetRenderer(wManager);
 
-    nb.content.rendermime.addRenderer(WIDGET_MIMETYPE, wRenderer, 0)
+    nb.content.rendermime.addRenderer(WIDGET_MIMETYPE, wRenderer, 0);
     return new DisposableDelegate(() => {
       nb.content.rendermime.removeRenderer(WIDGET_MIMETYPE);
       wRenderer.dispose();
       wManager.dispose();
-    })
+    });
   }
 }