diff --git a/packages/notebook-extension/src/index.ts b/packages/notebook-extension/src/index.ts index 52eac92d5c..e4d217c577 100644 --- a/packages/notebook-extension/src/index.ts +++ b/packages/notebook-extension/src/index.ts @@ -402,6 +402,51 @@ const notebookToolsWidget: JupyterFrontEndPlugin = { }, }; +/** + * A plugin to update the tab icon based on the kernel status. + */ +const tabIcon: JupyterFrontEndPlugin = { + id: '@jupyter-notebook/notebook-extension:tab-icon', + autoStart: true, + requires: [INotebookTracker], + activate: (app: JupyterFrontEnd, tracker: INotebookTracker) => { + // the favicons are provided by Jupyter Server + const notebookIcon = ' /static/favicons/favicon-notebook.ico'; + const busyIcon = ' /static/favicons/favicon-busy-1.ico'; + + const updateBrowserFavicon = ( + status: ISessionContext.KernelDisplayStatus + ) => { + const link = document.querySelector( + "link[rel*='icon']" + ) as HTMLLinkElement; + switch (status) { + case 'busy': + link.href = busyIcon; + break; + case 'idle': + link.href = notebookIcon; + break; + } + }; + + const onChange = async () => { + const current = tracker.currentWidget; + const sessionContext = current?.sessionContext; + if (!sessionContext) { + return; + } + + sessionContext.statusChanged.connect(() => { + const status = sessionContext.kernelDisplayStatus; + updateBrowserFavicon(status); + }); + }; + + tracker.currentChanged.connect(onChange); + }, +}; + /** * A plugin that adds a Trusted indicator to the menu area */ @@ -441,8 +486,9 @@ const plugins: JupyterFrontEndPlugin[] = [ closeTab, kernelLogo, kernelStatus, - scrollOutput, notebookToolsWidget, + scrollOutput, + tabIcon, trusted, ];