Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add busy favicon when kernels are running #4361

Merged
merged 12 commits into from
May 25, 2018
19 changes: 18 additions & 1 deletion packages/application-extension/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,23 @@ const notfound: JupyterLabPlugin<void> = {
};


/**
* Change the favicon changing based on the busy status;
*/
const busy: JupyterLabPlugin<void> = {
id: '@jupyterlab/application-extension:faviconbusy',
activate: async (app: JupyterLab) => {
app.busySignal.connect((_, isBusy) => {
const filename = isBusy ? 'favicon-busy-1.ico' : 'favicon.ico';
const favicon = document.querySelector('link[rel="shortcut icon"]') as HTMLLinkElement;
favicon.href = `/static/base/images/${filename}`;
});
},
requires: [],
autoStart: true,
};


/**
* Add the main application commands.
*/
Expand Down Expand Up @@ -358,6 +375,6 @@ function addCommands(app: JupyterLab, palette: ICommandPalette): void {
/**
* Export the plugins as default.
*/
const plugins: JupyterLabPlugin<any>[] = [main, layout, router, notfound];
const plugins: JupyterLabPlugin<any>[] = [main, layout, router, notfound, busy];

export default plugins;
39 changes: 39 additions & 0 deletions packages/application/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
import {
ApplicationShell
} from './shell';
import { ISignal, Signal } from '@phosphor/signaling';

export { ApplicationShell } from './shell';
export { ILayoutRestorer, LayoutRestorer } from './layoutrestorer';
Expand All @@ -62,6 +63,7 @@ class JupyterLab extends Application<ApplicationShell> {
*/
constructor(options: JupyterLab.IOptions = {}) {
super({ shell: new ApplicationShell() });
this._busySignal = new Signal(this);
this._info = { ...JupyterLab.defaultInfo, ...options };
if (this._info.devMode) {
this.shell.addClass('jp-mod-devMode');
Expand Down Expand Up @@ -108,6 +110,21 @@ class JupyterLab extends Application<ApplicationShell> {
return this._dirtyCount > 0;
}

/**
* Whether the application is busy.
*/
get isBusy(): boolean {
return this._busyCount > 0;
}

/**
* Returns a signal for when application changes it's busy status.
*/
get busySignal(): ISignal<JupyterLab, boolean> {
return this._busySignal;
}


/**
* The information about the application.
*/
Expand Down Expand Up @@ -137,6 +154,26 @@ class JupyterLab extends Application<ApplicationShell> {
});
}

/**
* Set the application state to busy.
*
* @returns A disposable used to clear the dirty state for the caller.
*/
setBusy(): IDisposable {
const oldBusy = this.isBusy;
this._busyCount++;
if (this.isBusy !== oldBusy) {
this._busySignal.emit(this.isBusy);
}
return new DisposableDelegate(() => {
const oldBusy = this.isBusy;
this._busyCount--;
if (this.isBusy !== oldBusy) {
this._busySignal.emit(this.isBusy);
}
});
}

/**
* Register plugins from a plugin module.
*
Expand Down Expand Up @@ -171,6 +208,8 @@ class JupyterLab extends Application<ApplicationShell> {

private _info: JupyterLab.IInfo;
private _dirtyCount = 0;
private _busyCount = 0;
private _busySignal: Signal<JupyterLab, boolean>;
}


Expand Down
25 changes: 25 additions & 0 deletions packages/apputils/src/clientsession.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ class ClientSession implements IClientSession {
this._path = options.path || uuid();
this._type = options.type || '';
this._name = options.name || '';
this._setBusy = options.setBusy;
this._kernelPreference = options.kernelPreference || {};
}

Expand Down Expand Up @@ -739,6 +740,23 @@ class ClientSession implements IClientSession {
* Handle a change to the session status.
*/
private _onStatusChanged(): void {

// Set that this kernel is busy, if we haven't already
// If we have already, and now we aren't busy, dispose
// of the busy disposable.
if (this._setBusy) {
if (this.status === 'busy') {
if (!this._busyDisposable) {
this._busyDisposable = this._setBusy();
}
} else {
if (this._busyDisposable) {
this._busyDisposable.dispose();
this._busyDisposable = null;
}
}
}

this._statusChanged.emit(this.status);
}

Expand Down Expand Up @@ -773,6 +791,8 @@ class ClientSession implements IClientSession {
private _unhandledMessage = new Signal<this, KernelMessage.IMessage>(this);
private _propertyChanged = new Signal<this, 'path' | 'name' | 'type'>(this);
private _dialog: Dialog<any> | null = null;
private _setBusy: () => IDisposable | undefined;
private _busyDisposable: IDisposable | null = null;
}


Expand Down Expand Up @@ -810,6 +830,11 @@ namespace ClientSession {
* A kernel preference.
*/
kernelPreference?: IClientSession.IKernelPreference;

/**
* A function to call when the session becomes busy.
*/
setBusy?: () => IDisposable;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/console-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ function activateConsole(app: JupyterLab, mainMenu: IMainMenu, palette: ICommand

// The launcher callback.
let callback = (cwd: string, name: string) => {
return createConsole({ basePath: cwd, kernelPreference: { name } });
return createConsole({ basePath: cwd, kernelPreference: { name }});
};

// Add a launcher item if the launcher is available.
Expand Down Expand Up @@ -214,6 +214,7 @@ function activateConsole(app: JupyterLab, mainMenu: IMainMenu, palette: ICommand
contentFactory,
mimeTypeService: editorServices.mimeTypeService,
rendermime,
setBusy: app.setBusy.bind(app),
...options as Partial<ConsolePanel.IOptions>
});

Expand Down
9 changes: 8 additions & 1 deletion packages/console/src/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
import {
CodeConsole
} from './widget';
import { IDisposable } from '@phosphor/disposable';


/**
Expand Down Expand Up @@ -73,7 +74,8 @@ class ConsolePanel extends Panel {
path,
name: name || `Console ${count}`,
type: 'console',
kernelPreference: options.kernelPreference
kernelPreference: options.kernelPreference,
setBusy: options.setBusy
});

let resolver = new RenderMimeRegistry.UrlResolver({
Expand Down Expand Up @@ -222,6 +224,11 @@ namespace ConsolePanel {
* The service used to look up mime types.
*/
mimeTypeService: IEditorMimeTypeService;

/**
* A function to call when the kernel is busy.
*/
setBusy?: () => IDisposable;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/docmanager-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ const plugin: JupyterLabPlugin<IDocumentManager> = {
};
const registry = app.docRegistry;
const when = app.restored.then(() => void 0);
const docManager = new DocumentManager({ registry, manager, opener, when });
const docManager = new DocumentManager({ registry, manager, opener, when, setBusy: app.setBusy.bind(app) });

// Register the file operations commands.
addCommands(app, docManager, palette, opener, settingRegistry);
Expand Down
10 changes: 9 additions & 1 deletion packages/docmanager/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class DocumentManager implements IDisposable {
let widgetManager = new DocumentWidgetManager({ registry: this.registry });
widgetManager.activateRequested.connect(this._onActivateRequested, this);
this._widgetManager = widgetManager;
this._setBusy = options.setBusy;
}

/**
Expand Down Expand Up @@ -408,7 +409,8 @@ class DocumentManager implements IDisposable {
factory,
path,
kernelPreference,
modelDBFactory
modelDBFactory,
setBusy: this._setBusy
});
let handler = new SaveHandler({ context });
Private.saveHandlerProperty.set(context, handler);
Expand Down Expand Up @@ -510,6 +512,7 @@ class DocumentManager implements IDisposable {
private _isDisposed = false;
private _autosave = true;
private _when: Promise<void>;
private _setBusy: () => IDisposable;
}


Expand Down Expand Up @@ -542,6 +545,11 @@ namespace DocumentManager {
* A promise for when to start using the manager.
*/
when?: Promise<void>;

/**
* A function called when a kernel is busy.
*/
setBusy?: () => IDisposable;
}

/**
Expand Down
8 changes: 7 additions & 1 deletion packages/docregistry/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
path: this._path,
type: ext === '.ipynb' ? 'notebook' : 'file',
name: PathExt.basename(localPath),
kernelPreference: options.kernelPreference || { shouldStart: false }
kernelPreference: options.kernelPreference || { shouldStart: false },
setBusy: options.setBusy
});
this.session.propertyChanged.connect(this._onSessionChanged, this);
manager.contents.fileChanged.connect(this._onFileChanged, this);
Expand Down Expand Up @@ -730,6 +731,11 @@ export namespace Context {
* An optional callback for opening sibling widgets.
*/
opener?: (widget: Widget) => void;

/**
* A function to call when the kernel is busy.
*/
setBusy?: () => IDisposable;
}
}

Expand Down