Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6802960
fix: Fix deepnote notebook deserializer
tkislan Mar 18, 2026
2de7885
Add tests
tkislan Mar 18, 2026
bc2de06
Merge branch 'main' into tk/fix-project-notebook-picker
tkislan Mar 19, 2026
3a418c1
Fix file change watcher
tkislan Mar 19, 2026
9b5c107
Merge remote-tracking branch 'origin/main' into tk/fix-project-notebo…
tkislan Mar 19, 2026
77c0347
Improve error handling in DeepnoteFileChangeWatcher to prevent stale …
tkislan Mar 20, 2026
6dc77a0
Add unit tests for DeepnoteFileChangeWatcher to handle scenarios with…
tkislan Mar 20, 2026
8898be9
Format code
tkislan Mar 20, 2026
c74ffa7
Enhance error handling in DeepnoteFileChangeWatcher to check save ope…
tkislan Mar 23, 2026
8d061ac
Add post-snapshot read grace period in unit tests for DeepnoteFileCha…
tkislan Mar 23, 2026
5388ead
Remove the second markSelfWrite() on the workspace.save() path.
tkislan Mar 26, 2026
6d05f22
Merge branch 'main' into tk/fix-project-notebook-picker
tkislan Mar 26, 2026
c5342a3
Merge branch 'main' into tk/fix-project-notebook-picker
tkislan Mar 26, 2026
3e7dfc9
feat(deepnote): Add clearNotebookSelection method and update notebook…
tkislan Mar 27, 2026
c160ae3
Update test
tkislan Mar 27, 2026
0b2504a
refactor(deepnote): Improve mock child process and server output mana…
tkislan Mar 27, 2026
9d5c6c0
refactor(deepnote): Simplify notebook edit application logic
tkislan Mar 27, 2026
44bd482
feat(deepnote): Enhance notebook resolution management and error hand…
tkislan Mar 30, 2026
36cc1e6
refactor(deepnote): Improve notebook ID retrieval with zod validation
tkislan Mar 31, 2026
71579cf
refactor(deepnote): Improve variable naming and import organization i…
tkislan Mar 31, 2026
23fdcca
Fix notebook deserialization race conditions
tkislan Mar 31, 2026
824f471
feat(deepnote): Enhance testing for DeepnoteFileChangeWatcher and Dee…
tkislan Mar 31, 2026
c6d21e6
feat(deepnote): Add snapshot interaction tests for DeepnoteFileChange…
tkislan Apr 1, 2026
5d9d6e2
Fix cspell
tkislan Apr 1, 2026
4962616
Fix tests
tkislan Apr 1, 2026
552366b
Minor improvements
tkislan Apr 1, 2026
bd4d5fd
fix(deepnote): Enhance error handling for metadata restoration in Dee…
tkislan Apr 1, 2026
cb4644f
Merge remote-tracking branch 'origin/main' into tk/fix-project-notebo…
tkislan Apr 2, 2026
ec96f53
feat(deepnote): Add DeepnoteNotebookInfoStatusBar for displaying note…
tkislan Apr 2, 2026
d44c1aa
feat(deepnote): Add command to copy active notebook details
tkislan Apr 2, 2026
7815f42
Add a failing test for external change rerender
tkislan Apr 2, 2026
855f38c
Refactor self write mark handling
tkislan Apr 2, 2026
f72b0fa
feat(deepnote): Enhance notebook ID resolution with tab-based logic
tkislan Apr 2, 2026
8e6f679
feat(deepnote): Refactor notebook selection handling and enhance mism…
tkislan Apr 7, 2026
0396a2c
refactor(deepnote): Simplify DeepnoteActivationService and enhance no…
tkislan Apr 7, 2026
06d3197
refactor(deepnote): Streamline DeepnoteNotebookSerializer and remove …
tkislan Apr 7, 2026
f4b79d3
feat(deepnote): Add logging for pending notebook resolutions in Deepn…
tkislan Apr 7, 2026
c83af55
Reformat code
tkislan Apr 7, 2026
40f6c67
Fix test
tkislan Apr 7, 2026
46cbcda
feat(deepnote): Enhance notebook deserialization and verification logic
tkislan Apr 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions src/notebooks/deepnote/deepnoteSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,15 @@ export class DeepnoteNotebookSerializer implements NotebookSerializer {
* @returns The notebook ID to deserialize, or undefined if none found
*/
findCurrentNotebookId(projectId: string): string | undefined {
// Prefer the active notebook editor when it matches the project
// Check the manager's stored selection first - this is set explicitly when the user
// picks a notebook from the explorer, and must take priority over the active editor
const storedNotebookId = this.notebookManager.getTheSelectedNotebookForAProject(projectId);

if (storedNotebookId) {
return storedNotebookId;
}

// Fallback: prefer the active notebook editor when it matches the project
const activeEditorNotebook = window.activeNotebookEditor?.notebook;

if (
Expand All @@ -199,14 +207,7 @@ export class DeepnoteNotebookSerializer implements NotebookSerializer {
return activeEditorNotebook.metadata.deepnoteNotebookId;
}

// Check the manager's stored selection - this should be set when opening from explorer
const storedNotebookId = this.notebookManager.getTheSelectedNotebookForAProject(projectId);

if (storedNotebookId) {
return storedNotebookId;
}

// Fallback: Check if there's an active notebook document for this project
// Last fallback: Check if there's an active notebook document for this project
const openNotebook = workspace.notebookDocuments.find(
(doc) => doc.notebookType === 'deepnote' && doc.metadata?.deepnoteProjectId === projectId
);
Expand Down
79 changes: 68 additions & 11 deletions src/notebooks/deepnote/deepnoteSerializer.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,11 +275,9 @@ project:
assert.strictEqual(result2, 'notebook-2');
});

test('should prioritize active notebook editor over stored selection', () => {
// Store a selection for the project
test('should prioritize stored selection over active editor', () => {
manager.selectNotebookForProject('project-123', 'stored-notebook');

// Mock the active notebook editor to return a different notebook
const mockActiveNotebook = {
notebookType: 'deepnote',
metadata: {
Expand All @@ -294,14 +292,30 @@ project:

const result = serializer.findCurrentNotebookId('project-123');

// Should return the active editor's notebook, not the stored one
assert.strictEqual(result, 'stored-notebook');
});

test('should return active editor notebook when no stored selection exists', () => {
const mockActiveNotebook = {
notebookType: 'deepnote',
metadata: {
deepnoteProjectId: 'project-123',
deepnoteNotebookId: 'active-editor-notebook'
}
};

when(mockedVSCodeNamespaces.window.activeNotebookEditor).thenReturn({
notebook: mockActiveNotebook
} as any);

const result = serializer.findCurrentNotebookId('project-123');

assert.strictEqual(result, 'active-editor-notebook');
});

test('should ignore active editor when project ID does not match', () => {
manager.selectNotebookForProject('project-123', 'stored-notebook');

// Mock active editor with a different project
const mockActiveNotebook = {
notebookType: 'deepnote',
metadata: {
Expand All @@ -316,14 +330,12 @@ project:

const result = serializer.findCurrentNotebookId('project-123');

// Should fall back to stored selection since active editor is for different project
assert.strictEqual(result, 'stored-notebook');
});

test('should ignore active editor when notebook type is not deepnote', () => {
manager.selectNotebookForProject('project-123', 'stored-notebook');

// Mock active editor with non-deepnote notebook type
const mockActiveNotebook = {
notebookType: 'jupyter-notebook',
metadata: {
Expand All @@ -338,19 +350,16 @@ project:

const result = serializer.findCurrentNotebookId('project-123');

// Should fall back to stored selection since active editor is not a deepnote notebook
assert.strictEqual(result, 'stored-notebook');
});

test('should ignore active editor when notebook ID is missing', () => {
manager.selectNotebookForProject('project-123', 'stored-notebook');

// Mock active editor without notebook ID in metadata
const mockActiveNotebook = {
notebookType: 'deepnote',
metadata: {
deepnoteProjectId: 'project-123'
// Missing deepnoteNotebookId
}
};

Expand All @@ -360,9 +369,57 @@ project:

const result = serializer.findCurrentNotebookId('project-123');

// Should fall back to stored selection since active editor has no notebook ID
assert.strictEqual(result, 'stored-notebook');
});

test('switching notebooks: selecting a different notebook while one is open should return the new selection', () => {
manager.selectNotebookForProject('project-123', 'notebook-A');

const mockActiveNotebook = {
notebookType: 'deepnote',
metadata: {
deepnoteProjectId: 'project-123',
deepnoteNotebookId: 'notebook-A'
}
};

when(mockedVSCodeNamespaces.window.activeNotebookEditor).thenReturn({
notebook: mockActiveNotebook
} as any);

assert.strictEqual(serializer.findCurrentNotebookId('project-123'), 'notebook-A');

manager.selectNotebookForProject('project-123', 'notebook-B');

assert.strictEqual(
serializer.findCurrentNotebookId('project-123'),
'notebook-B',
'Should return the newly selected notebook, not the one currently in the active editor'
);
});

test('switching notebooks: rapidly switching between three notebooks should always return the latest selection', () => {
const mockActiveNotebook = {
notebookType: 'deepnote',
metadata: {
deepnoteProjectId: 'project-123',
deepnoteNotebookId: 'notebook-1'
}
};

when(mockedVSCodeNamespaces.window.activeNotebookEditor).thenReturn({
notebook: mockActiveNotebook
} as any);

manager.selectNotebookForProject('project-123', 'notebook-1');
assert.strictEqual(serializer.findCurrentNotebookId('project-123'), 'notebook-1');

manager.selectNotebookForProject('project-123', 'notebook-2');
assert.strictEqual(serializer.findCurrentNotebookId('project-123'), 'notebook-2');

manager.selectNotebookForProject('project-123', 'notebook-3');
assert.strictEqual(serializer.findCurrentNotebookId('project-123'), 'notebook-3');
});
});

suite('component integration', () => {
Expand Down
Loading