@@ -9,13 +9,13 @@ import {
99 NotebookControllerAffinity ,
1010 window ,
1111 ProgressLocation ,
12- notebooks ,
1312 NotebookController ,
1413 CancellationTokenSource ,
1514 Disposable ,
1615 Uri ,
1716 l10n ,
18- env
17+ env ,
18+ commands
1919} from 'vscode' ;
2020import { IExtensionSyncActivationService } from '../../platform/activation/types' ;
2121import { IDisposableRegistry } from '../../platform/common/types' ;
@@ -122,17 +122,6 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
122122
123123 logger . info ( `Deepnote notebook opened: ${ getDisplayPath ( notebook . uri ) } ` ) ;
124124
125- // Check if we already have a controller ready for this notebook
126- const baseFileUri = notebook . uri . with ( { query : '' , fragment : '' } ) ;
127- const notebookKey = baseFileUri . fsPath ;
128- const hasExistingController = this . notebookControllers . has ( notebookKey ) ;
129-
130- // If no existing controller, create a temporary "Loading" controller immediately
131- // This prevents the kernel selector from appearing when user clicks Run All
132- if ( ! hasExistingController ) {
133- this . createLoadingController ( notebook , notebookKey ) ;
134- }
135-
136125 // Always try to ensure kernel is selected (this will reuse existing controllers)
137126 // Don't await - let it happen in background so notebook opens quickly
138127 void this . ensureKernelSelected ( notebook ) . catch ( ( error ) => {
@@ -301,20 +290,22 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
301290 }
302291
303292 public async ensureKernelSelected ( notebook : NotebookDocument , _token ?: CancellationToken ) : Promise < void > {
304- return window . withProgress (
293+ const baseFileUri = notebook . uri . with ( { query : '' , fragment : '' } ) ;
294+ const notebookKey = baseFileUri . fsPath ;
295+
296+ const kernelSelected = await window . withProgress (
305297 {
306298 location : ProgressLocation . Notification ,
307299 title : l10n . t ( 'Loading Deepnote Kernel' ) ,
308300 cancellable : true
309301 } ,
310- async ( progress , progressToken ) => {
302+ async ( progress , progressToken ) : Promise < boolean > => {
311303 try {
312304 logger . info ( `Ensuring Deepnote kernel is selected for ${ getDisplayPath ( notebook . uri ) } ` ) ;
313305
314306 // Extract the base file URI (without query parameters)
315307 // Notebooks from the same .deepnote file have different URIs with ?notebook=id query params
316- const baseFileUri = notebook . uri . with ( { query : '' , fragment : '' } ) ;
317- const notebookKey = baseFileUri . fsPath ;
308+
318309 logger . info ( `Base Deepnote file: ${ getDisplayPath ( baseFileUri ) } ` ) ;
319310
320311 // Check if we already have a controller for this notebook file
@@ -345,7 +336,7 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
345336 const selectedController = this . controllerRegistration . getSelected ( notebook ) ;
346337 if ( selectedController && selectedController . id === existingController . id ) {
347338 logger . info ( `Controller already selected for ${ getDisplayPath ( notebook . uri ) } ` ) ;
348- return ;
339+ return true ;
349340 }
350341
351342 // Auto-select the existing controller for this notebook
@@ -363,7 +354,7 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
363354 logger . info ( `Disposed loading controller for ${ notebookKey } ` ) ;
364355 }
365356
366- return ;
357+ return true ;
367358 }
368359
369360 // No existing controller - check if user has selected a configuration for this notebook
@@ -387,10 +378,10 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
387378 selectedConfig = await this . configurationPicker . pickEnvironment ( baseFileUri ) ;
388379
389380 if ( ! selectedConfig ) {
390- logger . info ( `User cancelled configuration selection - no kernel will be loaded` ) ;
391- throw new Error (
392- 'No environment selected. Please create an environment using the Deepnote Environments view.'
381+ logger . info (
382+ `User cancelled configuration selection or no environment found - no kernel will be loaded`
393383 ) ;
384+ return false ;
394385 }
395386
396387 // Save the selection
@@ -401,20 +392,61 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
401392 }
402393
403394 // Use the selected configuration
404- return this . ensureKernelSelectedWithConfiguration (
395+ await this . ensureKernelSelectedWithConfiguration (
405396 notebook ,
406397 selectedConfig ,
407398 baseFileUri ,
408399 notebookKey ,
409400 progress ,
410401 progressToken
411402 ) ;
403+
404+ return true ;
412405 } catch ( ex ) {
413406 logger . error ( 'Failed to auto-select Deepnote kernel' , ex ) ;
414407 throw ex ;
415408 }
416409 }
417410 ) ;
411+
412+ if ( ! kernelSelected ) {
413+ const createLabel = l10n . t ( 'Create Environment' ) ;
414+ const cancelLabel = l10n . t ( 'Cancel' ) ;
415+
416+ const choice = await window . showInformationMessage (
417+ l10n . t ( 'No environments found. Create one to use with {0}?' , getDisplayPath ( baseFileUri ) ) ,
418+ createLabel ,
419+ cancelLabel
420+ ) ;
421+
422+ if ( choice === createLabel ) {
423+ // Trigger the create command
424+ logger . info ( 'Triggering create environment command from picker' ) ;
425+ await commands . executeCommand ( 'deepnote.environments.create' ) ;
426+
427+ const selectedConfig = await this . configurationPicker . pickEnvironment ( baseFileUri ) ;
428+ if ( ! selectedConfig ) {
429+ return ;
430+ }
431+
432+ const tmpCancellation = new CancellationTokenSource ( ) ;
433+ const tmpCancellationToken = tmpCancellation . token ;
434+
435+ // Use the selected configuration
436+ await this . ensureKernelSelectedWithConfiguration (
437+ notebook ,
438+ selectedConfig ,
439+ baseFileUri ,
440+ notebookKey ,
441+ {
442+ report : ( ) => {
443+ logger . info ( 'Progress report' ) ;
444+ }
445+ } ,
446+ tmpCancellationToken
447+ ) ;
448+ }
449+ }
418450 }
419451
420452 private async ensureKernelSelectedWithConfiguration (
@@ -627,31 +659,6 @@ export class DeepnoteKernelAutoSelector implements IDeepnoteKernelAutoSelector,
627659 return kernelSpec ;
628660 }
629661
630- private createLoadingController ( notebook : NotebookDocument , notebookKey : string ) : void {
631- // Create a temporary controller that shows "Loading..." and prevents kernel selection prompt
632- const loadingController = notebooks . createNotebookController (
633- `deepnote-loading-${ notebookKey } ` ,
634- DEEPNOTE_NOTEBOOK_TYPE ,
635- l10n . t ( 'Loading Deepnote Kernel...' )
636- ) ;
637-
638- // Set it as the preferred controller immediately
639- loadingController . supportsExecutionOrder = false ;
640- loadingController . supportedLanguages = [ 'python' ] ;
641-
642- // Execution handler that does nothing - cells will just sit there until real kernel is ready
643- loadingController . executeHandler = ( ) => {
644- // No-op: execution is blocked until the real controller takes over
645- } ;
646-
647- // Select this controller for the notebook
648- loadingController . updateNotebookAffinity ( notebook , NotebookControllerAffinity . Preferred ) ;
649-
650- // Store it so we can dispose it later
651- this . loadingControllers . set ( notebookKey , loadingController ) ;
652- logger . info ( `Created loading controller for ${ notebookKey } ` ) ;
653- }
654-
655662 /**
656663 * Clear the controller selection for a notebook using a specific environment.
657664 * This is used when deleting an environment to unselect its controller from any open notebooks.
0 commit comments