diff --git a/app/package.json b/app/package.json index cf8ef5cefb1..821bf16a962 100644 --- a/app/package.json +++ b/app/package.json @@ -328,6 +328,9 @@ "@jupyterlab/filebrowser-extension:settings", "@jupyterlab/filebrowser-extension:share-file" ], + "@jupyterlab/notebook-extension": [ + "@jupyterlab/notebook-extension:open-with-no-kernel" + ], "@jupyter-notebook/tree-extension": true, "@jupyterlab/running-extension": [ "@jupyterlab/running-extension:plugin" diff --git a/packages/application-extension/src/index.ts b/packages/application-extension/src/index.ts index 6ba7a95bcf5..24c65294f32 100644 --- a/packages/application-extension/src/index.ts +++ b/packages/application-extension/src/index.ts @@ -269,9 +269,19 @@ const opener: JupyterFrontEndPlugin = { } const factory = urlParams.get('factory') ?? defaultFactory; - docManager.open(file, factory, undefined, { - ref: '_noref', - }); + const kernelPreference = + urlParams.get('kernel') === 'none' + ? { shouldStart: false, shouldReuse: false } + : undefined; + docManager.openOrReveal( + file, + factory, + undefined, + { + ref: '_noref', + }, + kernelPreference + ); }); }, describedBy: { diff --git a/packages/docmanager-extension/src/index.ts b/packages/docmanager-extension/src/index.ts index bd21f625efe..e8d329c8d2a 100644 --- a/packages/docmanager-extension/src/index.ts +++ b/packages/docmanager-extension/src/index.ts @@ -72,6 +72,13 @@ const opener: JupyterFrontEndPlugin = { factory: widgetName, }); } + if ( + route === 'notebooks' && + widget.context.sessionContext.kernelPreference.shouldStart === false + ) { + searchParams ??= new URLSearchParams(); + searchParams.set('kernel', 'none'); + } pathOpener.open({ prefix: URLExt.join(baseUrl, route), diff --git a/ui-tests/test/filebrowser.spec.ts b/ui-tests/test/filebrowser.spec.ts index 41cb88ea69e..877e7954372 100644 --- a/ui-tests/test/filebrowser.spec.ts +++ b/ui-tests/test/filebrowser.spec.ts @@ -103,6 +103,40 @@ test.describe('File Browser', () => { await notebook.close(); }); + test('Open a notebook without starting a kernel', async ({ + page, + tmpPath, + }) => { + await page.filebrowser.refresh(); + + await page.getByText('empty.ipynb').last().click({ button: 'right' }); + await page.getByText('Open With', { exact: true }).hover(); + + const [notebook] = await Promise.all([ + page.waitForEvent('popup'), + page.getByRole('menuitem', { name: 'Notebook (no kernel)' }).click(), + ]); + + await notebook.waitForSelector('.jp-NotebookPanel'); + const hasRunningSession = await notebook.evaluate(async (notebookPath) => { + const app = window.jupyterapp; + await app.started; + const currentWidget = app.shell.currentWidget as unknown as { + sessionContext: { ready: Promise }; + }; + await currentWidget.sessionContext.ready; + await app.serviceManager.sessions.refreshRunning(); + return Array.from(app.serviceManager.sessions.running()).some( + (session) => session.path === notebookPath + ); + }, `${tmpPath}/empty.ipynb`); + + await expect(notebook.getByTitle('Switch kernel')).toHaveText('No Kernel'); + expect(hasRunningSession).toBe(false); + + await notebook.close(); + }); + test('Toggle the Date Created column from the header context menu', async ({ page, }) => {