Skip to content

Conversation

@Artmann
Copy link
Member

@Artmann Artmann commented Oct 8, 2025

Summary by CodeRabbit

  • New Features
    • Introduced an interactive Dataframe Renderer for notebooks with pagination and page-size controls, featuring improved styling.
  • Enhancements
    • Localized kernel setup messages and progress.
    • More reliable Python environment provisioning tied to toolkit version.
    • Smoother cell re-execution and output clearing behavior.
    • Smarter requirements.txt creation with normalization, prompts, and cancellation.
    • Preserves and applies table pagination state on re-run.
  • Bug Fixes
    • More consistent output updates across render paths.
  • Documentation
    • Added Deepnote Blocks Architecture guide; removed outdated serialization doc.
  • Chores
    • Updated CI permissions configuration.

@Artmann Artmann changed the title feat: Set up a custom renderer for data frames. feat: Deepnote style data frames Oct 8, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

📝 Walkthrough

Walkthrough

This PR introduces a new webview-side dataframe renderer with Tailwind-based styling and wires renderer messaging to an extension-side DataframeController for pagination/state updates. The build system adds PostCSS/Tailwind processing for specific CSS and a new esbuild entry for the dataframe renderer bundle. Kernel execution now generates Python code via @deepnote/blocks using blocks created from notebook cells, and output metadata propagation gains cellId/cellIndex to support renderers. Deepnote pocket handling moves id to top-level metadata and stops storing outputs in the pocket. Multiple components add localization, toolkit installer becomes version-aware, CI gains packages: read, and new docs (blocks.md) replace serialization.md.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Renderer as DataframeRenderer (webview)
  participant Messaging as VSCode Renderer Messaging
  participant Ext as DataframeController (extension)
  participant VSCode as VS Code Notebook API
  participant Kernel as Python Kernel

  User->>Renderer: Change page size / navigate
  Renderer->>Messaging: postMessage({ command, cellId, ... })
  Messaging->>Ext: onDidReceiveMessage({ editor, message })
  Ext->>VSCode: ApplyNotebookEdit(update deepnote_table_state)
  Ext->>VSCode: Re-execute cell (by cellId)
  VSCode->>Kernel: Execute generated code
  note over Kernel,VSCode: Code produced via createPythonCode(...)
  Kernel-->>VSCode: Outputs with metadata (cellId, cellIndex, ...)
  VSCode-->>Renderer: Render output item
  Renderer->>Renderer: Re-render table with new state
Loading
sequenceDiagram
  autonumber
  participant Cell as NotebookCell
  participant Converter as Pocket/Converter
  participant Blocks as createBlockFromPocket
  participant Codegen as createPythonCode (@deepnote/blocks)
  participant Kernel as Python Kernel

  Cell->>Converter: Extract pocket + top-level metadata (id at top-level)
  Converter->>Blocks: createBlockFromPocket(pocket, metadata)
  Blocks-->>Converter: DeepnoteBlock (outputs not taken from pocket)
  Converter->>Codegen: createPythonCode(block)
  Codegen-->>Kernel: Python code
  Kernel-->>Cell: Outputs (with cellId/cellIndex in metadata)
Loading

Possibly related PRs

Suggested reviewers

  • andyjakubowski
  • jankuca

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “feat: Deepnote style data frames” concisely conveys the primary enhancement of introducing Deepnote‐styled data frame rendering, matching the core addition of the DataframeRenderer and its styling setup. It is specific enough to inform reviewers of the main change without extraneous details. A teammate scanning PR history will immediately grasp that this pull request adds styled data frame support.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 338e054 and a422532.

📒 Files selected for processing (5)
  • build/esbuild/build.ts (1 hunks)
  • package.json (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/index.ts (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/styles.css (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility, then alphabetically
Separate third-party imports from local file imports

Files:

  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • build/esbuild/build.ts
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/webviews/webview-side/dataframe-renderer/index.ts
  • build/esbuild/build.ts
🧠 Learnings (1)
📚 Learning: 2025-09-12T12:52:09.726Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T12:52:09.726Z
Learning: Applies to src/notebooks/deepnote/deepnoteActivationService.ts : deepnoteActivationService.ts wires VSCode activation

Applied to files:

  • src/webviews/webview-side/dataframe-renderer/index.ts
🧬 Code graph analysis (2)
src/webviews/webview-side/dataframe-renderer/index.ts (1)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1)
  • DataframeRenderer (7-11)
build/esbuild/build.ts (6)
gulpfile.js (1)
  • path (14-14)
build/launchWebUtils.js (1)
  • path (4-4)
build/preLaunchWebTest.js (1)
  • path (4-4)
src/kernels/common/baseJupyterSessionConnection.ts (1)
  • path (23-25)
build/ci/postInstall.js (1)
  • path (8-8)
build/webpack/common.js (1)
  • path (6-6)
🔇 Additional comments (2)
src/webviews/webview-side/dataframe-renderer/styles.css (1)

1-42: LGTM!

Comprehensive dataframe table styling with proper VSCode theme integration.

package.json (1)

1812-1821: LGTM!

Renderer registration correctly configured. Entrypoint path matches the build output.

const root = document.createElement('div');
element.appendChild(root);

ReactDOM.render(React.createElement(DataframeRenderer, { data }), root);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add React cleanup in disposeOutputItem.

ReactDOM.render mounts a component but disposeOutputItem doesn't unmount it, causing memory leaks when outputs are disposed.

Store the root element reference and call ReactDOM.unmountComponentAtNode(root) in disposeOutputItem:

 export const activate: ActivationFunction = (_context: RendererContext<unknown>) => {
+    const roots = new Map<string, HTMLElement>();
+
     return {
         renderOutputItem(outputItem: OutputItem, element: HTMLElement) {
             try {
                 const data = outputItem.json();
                 console.log('Dataframe renderer - received data:', data);
 
                 const root = document.createElement('div');
                 element.appendChild(root);
+                roots.set(outputItem.id, root);
 
                 ReactDOM.render(React.createElement(DataframeRenderer, { data }), root);
             } catch (error) {
                 console.error('Error rendering dataframe:', error);
                 const errorDiv = document.createElement('div');
                 errorDiv.style.padding = '10px';
                 errorDiv.style.color = 'var(--vscode-errorForeground)';
                 errorDiv.textContent = `Error rendering dataframe: ${error}`;
                 element.appendChild(errorDiv);
             }
         },
 
-        disposeOutputItem(_id?: string) {
-            // Cleanup if needed
+        disposeOutputItem(id?: string) {
+            if (id) {
+                const root = roots.get(id);
+                if (root) {
+                    ReactDOM.unmountComponentAtNode(root);
+                    roots.delete(id);
+                }
+            }
         }
     };
 };

Also applies to: 31-33

🤖 Prompt for AI Agents
In src/webviews/webview-side/dataframe-renderer/index.ts around lines 20 and
also lines 31-33, ReactDOM.render is used to mount DataframeRenderer but
disposeOutputItem never unmounts it, causing memory leaks; modify the module to
store the root DOM element (or React root reference) when rendering and then
call ReactDOM.unmountComponentAtNode(root) inside disposeOutputItem (or use the
appropriate ReactDOM.unmount API for your React version) to unmount and clean up
the component before removing the element.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

♻️ Duplicate comments (3)
src/webviews/webview-side/dataframe-renderer/index.ts (3)

13-16: Use ILogger service instead of console.log.

Per coding guidelines, use ILogger for logging.

As per coding guidelines.


23-23: Use ILogger service instead of console.error.

Per coding guidelines, use ILogger for error logging.

As per coding guidelines.


21-21: Add React cleanup in disposeOutputItem.

ReactDOM.render mounts components without unmounting in disposeOutputItem, causing memory leaks.

Store root element and call ReactDOM.unmountComponentAtNode(root) in disposeOutputItem. See previous review for full diff.

Also applies to: 32-34

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a422532 and 3f9c6af.

📒 Files selected for processing (10)
  • package.json (1 hunks)
  • src/kernels/deepnote/deepnoteController.ts (1 hunks)
  • src/kernels/execution/cellExecution.ts (1 hunks)
  • src/notebooks/controllers/vscodeNotebookController.ts (3 hunks)
  • src/webviews/extension-side/dataframe/rendererComms.ts (1 hunks)
  • src/webviews/extension-side/serviceRegistry.node.ts (2 hunks)
  • src/webviews/extension-side/serviceRegistry.web.ts (2 hunks)
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/index.ts (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/styles.css (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.web.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.web.ts for Web-specific implementations using browser-compatible APIs

Files:

  • src/webviews/extension-side/serviceRegistry.web.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility, then alphabetically
Separate third-party imports from local file imports

Files:

  • src/webviews/extension-side/serviceRegistry.web.ts
  • src/kernels/deepnote/deepnoteController.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
  • src/webviews/extension-side/serviceRegistry.node.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/webviews/extension-side/dataframe/rendererComms.ts
  • src/kernels/execution/cellExecution.ts
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/kernels/deepnote/deepnoteController.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/webviews/extension-side/dataframe/rendererComms.ts
  • src/kernels/execution/cellExecution.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/deepnote/deepnoteController.ts
  • src/kernels/execution/cellExecution.ts
**/*.node.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.node.ts for Desktop-specific implementations that require full file system access and Python environments

Files:

  • src/webviews/extension-side/serviceRegistry.node.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecution.ts
🧠 Learnings (9)
📚 Learning: 2025-09-03T12:55:29.175Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/ipywidgets.instructions.md:0-0
Timestamp: 2025-09-03T12:55:29.175Z
Learning: Applies to src/webviews/extension-side/ipywidgets/rendererComms.ts : RendererComms should track widget model IDs in outputs, handle renderer queries, and coordinate display messages with IPyWidgetMessageDispatcher

Applied to files:

  • src/webviews/extension-side/serviceRegistry.web.ts
  • src/webviews/extension-side/dataframe/rendererComms.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Applied to files:

  • src/kernels/deepnote/deepnoteController.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/kernels/execution/cellExecution.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : For new kernel sources, implement ILocalNotebookKernelSourceSelector (or similar)

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : Create a UI provider for kernel selection for the new kernel source

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : Register the new kernel source in KernelSourceCommandHandler

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:54:35.368Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/interactiveWindow.instructions.md:0-0
Timestamp: 2025-09-03T12:54:35.368Z
Learning: Applies to src/interactive-window/interactiveWindowController.ts : InteractiveWindowController must manage kernel selection/startup/lifecycle, system info cell handling, restart logic, and maintain owner file associations for kernel context

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/kernelProvider.base.ts : KernelProviderBase must create/manage IKernel instances, track by NotebookDocument, manage lifecycle (start/restart/dispose), and expose INotebookKernelExecution

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use the `ILogger` service instead of `console.log`

Applied to files:

  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
  • src/webviews/webview-side/dataframe-renderer/index.ts
📚 Learning: 2025-09-12T12:52:09.726Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T12:52:09.726Z
Learning: Applies to src/notebooks/deepnote/deepnoteActivationService.ts : deepnoteActivationService.ts wires VSCode activation

Applied to files:

  • src/webviews/webview-side/dataframe-renderer/index.ts
🧬 Code graph analysis (6)
src/webviews/extension-side/serviceRegistry.web.ts (1)
src/platform/activation/types.ts (2)
  • IExtensionSyncActivationService (15-15)
  • IExtensionSyncActivationService (22-24)
src/kernels/deepnote/deepnoteController.ts (1)
src/kernels/kernelController.ts (1)
  • KernelController (7-15)
src/notebooks/controllers/vscodeNotebookController.ts (2)
src/kernels/deepnote/deepnoteController.ts (1)
  • DeepnoteController (19-28)
src/kernels/types.ts (1)
  • IKernelController (930-933)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (3)
gulpfile.js (1)
  • key (160-160)
src/test/mocks/vsc/extHostedTypes.ts (1)
  • updateCellMetadata (2480-2482)
src/test/datascience/ipywidgets/samples/jupyter-require/core.js (1)
  • context (348-351)
src/webviews/extension-side/serviceRegistry.node.ts (1)
src/platform/activation/types.ts (2)
  • IExtensionSyncActivationService (15-15)
  • IExtensionSyncActivationService (22-24)
src/webviews/webview-side/dataframe-renderer/index.ts (2)
src/webviews/extension-side/dataframe/rendererComms.ts (1)
  • activate (49-52)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1)
  • DataframeRenderer (23-70)
🪛 Biome (2.1.2)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx

[error] 42-42: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

🪛 GitHub Actions: CI
src/kernels/deepnote/deepnoteController.ts

[error] 20-20: ESLint: Useless constructor. Remove the constructor or use it to initialize fields. @typescript-eslint/no-useless-constructor

src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx

[error] 24-24: TS6133: 'resultsPerPage' is declared but its value is never read.

🪛 GitHub Check: Lint & Format
src/kernels/deepnote/deepnoteController.ts

[failure] 26-26:
'DeepnoteNotebookCellExecution' was used before it was defined


[failure] 20-20:
Useless constructor

src/webviews/extension-side/dataframe/rendererComms.ts

[failure] 72-72:
Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator


[failure] 66-66:
Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator

🔇 Additional comments (14)
package.json (1)

1812-1821: LGTM!

Notebook renderer registration is correct. MIME type and entry point align with the new dataframe renderer implementation.

src/notebooks/controllers/vscodeNotebookController.ts (1)

551-551: LGTM!

DeepnoteController integration follows the existing pattern. The wrapper intercepts cell execution at the right points.

Also applies to: 564-564

src/webviews/extension-side/serviceRegistry.web.ts (1)

30-30: LGTM!

Service registration follows the established pattern for renderer communication services.

Also applies to: 69-72

src/webviews/extension-side/serviceRegistry.node.ts (1)

20-20: LGTM!

Mirrors web registry. Consistent service registration across runtimes.

Also applies to: 69-72

src/webviews/extension-side/dataframe/rendererComms.ts (7)

1-15: Imports follow guidelines.

Third-party and local imports are properly separated, and ILogger is correctly used.


17-39: Well-structured command types.

Clear, type-safe definitions for all commands.


41-47: Proper lifecycle management.

Injectable service with correct disposal pattern.


49-52: Activation setup is correct.

Renderer messaging channel established with proper event subscription.


80-98: Cell metadata update is correct.

Proper use of WorkspaceEdit and NotebookEdit APIs with null checking.


100-108: Placeholder ready for enhancement.

Current implementation logs activity. Comment indicates future metadata storage option.


110-113: Clipboard integration is correct.

Proper use of VS Code clipboard API.

src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (3)

1-2: Imports are correct.


4-21: Props interface is well-defined.

Clear type safety for renderer props.


44-67: Rendering logic is correct.

Column filtering, row mapping, and layout structure are properly implemented.

Comment on lines 20 to 22
constructor(controller: NotebookController) {
super(controller);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove useless constructor.

Constructor only calls super(). TypeScript doesn't require explicit super() constructor when no additional logic is needed.

Apply this diff:

 export class DeepnoteController extends KernelController {
-    constructor(controller: NotebookController) {
-        super(controller);
-    }
-
     public override createNotebookCellExecution(cell: NotebookCell): NotebookCellExecution {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
constructor(controller: NotebookController) {
super(controller);
}
export class DeepnoteController extends KernelController {
public override createNotebookCellExecution(cell: NotebookCell): NotebookCellExecution {
🧰 Tools
🪛 GitHub Actions: CI

[error] 20-20: ESLint: Useless constructor. Remove the constructor or use it to initialize fields. @typescript-eslint/no-useless-constructor

🪛 GitHub Check: Lint & Format

[failure] 20-20:
Useless constructor

🤖 Prompt for AI Agents
In src/kernels/deepnote/deepnoteController.ts around lines 20 to 22, the
constructor only calls super(controller) and is therefore redundant; remove the
explicit constructor entirely so the class uses the implicit parent constructor,
leaving no other logic changes.

Comment on lines 64 to 77
switch (message.command) {
case 'selectPageSize':
this.handleSelectPageSize(editor, message);
break;
case 'goToPage':
this.handleGoToPage(editor, message);
break;
case 'copyTableData':
this.handleCopyTableData(message);
break;
case 'exportDataframe':
this.handleExportDataframe(editor, message);
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Await async handlers to prevent silent failures.

handleSelectPageSize and handleCopyTableData are async but called without await. Errors will be silently swallowed, and execution order becomes non-deterministic.

Apply this diff:

         switch (message.command) {
             case 'selectPageSize':
-                this.handleSelectPageSize(editor, message);
+                void this.handleSelectPageSize(editor, message);
                 break;
             case 'goToPage':
                 this.handleGoToPage(editor, message);
                 break;
             case 'copyTableData':
-                this.handleCopyTableData(message);
+                void this.handleCopyTableData(message);
                 break;
             case 'exportDataframe':
                 this.handleExportDataframe(editor, message);
                 break;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
switch (message.command) {
case 'selectPageSize':
this.handleSelectPageSize(editor, message);
break;
case 'goToPage':
this.handleGoToPage(editor, message);
break;
case 'copyTableData':
this.handleCopyTableData(message);
break;
case 'exportDataframe':
this.handleExportDataframe(editor, message);
break;
}
switch (message.command) {
case 'selectPageSize':
void this.handleSelectPageSize(editor, message);
break;
case 'goToPage':
this.handleGoToPage(editor, message);
break;
case 'copyTableData':
void this.handleCopyTableData(message);
break;
case 'exportDataframe':
this.handleExportDataframe(editor, message);
break;
}
🧰 Tools
🪛 GitHub Check: Lint & Format

[failure] 72-72:
Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator


[failure] 66-66:
Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator

🤖 Prompt for AI Agents
In src/webviews/extension-side/dataframe/rendererComms.ts around lines 64 to 77,
async handlers (handleSelectPageSize and handleCopyTableData) are invoked
without awaiting, which can swallow errors and make ordering non-deterministic;
update the switch to await these async calls (e.g., await
this.handleSelectPageSize(editor, message); await
this.handleCopyTableData(message);) and ensure the enclosing function is
declared async (or otherwise returns/handles the resulting Promise) so errors
propagate and execution order is preserved.

Comment on lines 115 to 123
private handleExportDataframe(editor: NotebookEditor, message: ExportDataframeCommand) {
const cell = editor.notebook.cellAt(message.cellIndex);
logger.info(
`[DataframeRenderer] exportDataframe called for cell ${
message.cellIndex
} (${cell?.document.uri.toString()})`
);
// TODO: Implement dataframe export functionality
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

TODO: Implement export functionality.

Export feature is a placeholder. Consider opening an issue to track this requirement.

Do you want me to open an issue to track the dataframe export implementation?

@codecov
Copy link

codecov bot commented Oct 13, 2025

Codecov Report

❌ Patch coverage is 79.70297% with 41 lines in your changes missing coverage. Please review.
✅ Project coverage is 70%. Comparing base (d1ca48b) to head (8309267).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ernels/execution/cellExecutionCreator.unit.test.ts 80% 24 Missing ⚠️
...c/kernels/execution/cellExecutionMessageHandler.ts 56% 3 Missing and 4 partials ⚠️
src/kernels/execution/cellExecution.ts 28% 5 Missing ⚠️
src/kernels/execution/cellExecutionCreator.ts 84% 1 Missing and 1 partial ⚠️
src/notebooks/deepnote/deepnoteDataConverter.ts 88% 2 Missing ⚠️
src/kernels/execution/helpers.ts 93% 1 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff           @@
##            main     #28    +/-   ##
======================================
  Coverage     70%     70%            
======================================
  Files        512     513     +1     
  Lines      37506   37667   +161     
  Branches    4811    4819     +8     
======================================
+ Hits       26473   26621   +148     
- Misses      9441    9447     +6     
- Partials    1592    1599     +7     
Files with missing lines Coverage Δ
...ebooks/deepnote/deepnoteDataConverter.unit.test.ts 100% <100%> (ø)
src/notebooks/deepnote/pocket.ts 100% <100%> (ø)
src/notebooks/deepnote/pocket.unit.test.ts 100% <100%> (ø)
...ne/intellisense/resolveCompletionItem.unit.test.ts 100% <100%> (ø)
src/kernels/execution/helpers.ts 48% <93%> (+1%) ⬆️
src/kernels/execution/cellExecutionCreator.ts 82% <84%> (+17%) ⬆️
src/notebooks/deepnote/deepnoteDataConverter.ts 54% <88%> (+11%) ⬆️
src/kernels/execution/cellExecution.ts 10% <28%> (+<1%) ⬆️
...c/kernels/execution/cellExecutionMessageHandler.ts 40% <56%> (+<1%) ⬆️
...ernels/execution/cellExecutionCreator.unit.test.ts 80% <80%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts (2)

365-369: Avoid fsPath in provider handle (stability/portability)

Handles should not contain OS-specific paths/backslashes. Use the venv hash for a stable identifier.

-                    const serverProviderHandle: JupyterServerProviderHandle = {
+                    const serverProviderHandle: JupyterServerProviderHandle = {
                         extensionId: JVSC_EXTENSION_ID,
                         id: 'deepnote-server',
-                        handle: `deepnote-toolkit-server-${baseFileUri.fsPath}`
+                        handle: `deepnote-toolkit-server-${this.toolkitInstaller.getVenvHash(baseFileUri)}`
                     };

Based on learnings


524-547: Sanitize loading controller id

Controller id embeds full file path. Prefer a short, stable id (e.g., use venv hash) to avoid length/special-char issues.

-        const loadingController = notebooks.createNotebookController(
-            `deepnote-loading-${notebookKey}`,
+        const loadingController = notebooks.createNotebookController(
+            `deepnote-loading-${this.toolkitInstaller.getVenvHash(notebook.uri.with({ query: '', fragment: '' }))}`,
             DEEPNOTE_NOTEBOOK_TYPE,
             'Loading Deepnote Kernel...'
         );

As per coding guidelines

♻️ Duplicate comments (6)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (3)

46-46: Replace console.log with ILogger service.

Multiple console.log statements violate coding guidelines.

As per coding guidelines.

Also applies to: 52-52, 65-65, 74-74, 82-82, 91-91


153-164: Add explicit button type.

Missing type="button" can cause unintended form submission.

Apply this diff:

                 <button
+                    type="button"
                     className={`

170-181: Add explicit button type.

Missing type="button" can cause unintended form submission.

Apply this diff:

                 <button
+                    type="button"
                     className={`
src/webviews/webview-side/dataframe-renderer/index.ts (2)

21-55: Replace console logging with ILogger

Console logging breaches the project logging rule—wire this through ILogger (or the approved webview logging bridge) instead.
As per coding guidelines


18-61: Unmount React roots on dispose

Roots stay mounted, leaking listeners. Track each outputItem.id, call ReactDOM.unmountComponentAtNode, and drop the node during disposeOutputItem.

-export const activate: ActivationFunction = (context: RendererContext<unknown>) => {
-    return {
+export const activate: ActivationFunction = (context: RendererContext<unknown>) => {
+    const roots = new Map<string, HTMLElement>();
+
+    return {
         renderOutputItem(outputItem: OutputItem, element: HTMLElement) {
@@
-                ReactDOM.render(
+                ReactDOM.render(
                     React.createElement(DataframeRenderer, {
                         context,
                         data,
                         metadata: dataFrameMetadata,
                         cellId,
                         cellIndex
                     }),
                     root
                 );
+                if (outputItem.id) {
+                    roots.set(outputItem.id, root);
+                }
             } catch (error) {
@@
-        disposeOutputItem(_id?: string) {
-            // Cleanup if needed
+        disposeOutputItem(id?: string) {
+            if (!id) {
+                return;
+            }
+            const root = roots.get(id);
+            if (root) {
+                ReactDOM.unmountComponentAtNode(root);
+                root.remove();
+                roots.delete(id);
+            }
         }
     };
 };
build/esbuild/build.ts (1)

321-324: Watch flag should follow other webview builds.

This renderer should also use watch: watchAll so it rebuilds under npm run watch-all, just like the other webview bundles. The earlier feedback still applies.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f9c6af and f93fb09.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (31)
  • CLAUDE.md (1 hunks)
  • DEEPNOTE_KERNEL_IMPLEMENTATION.md (5 hunks)
  • README.md (1 hunks)
  • build/esbuild/build.ts (5 hunks)
  • package.json (5 hunks)
  • postcss.config.js (1 hunks)
  • src/extension.common.ts (2 hunks)
  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts (6 hunks)
  • src/kernels/deepnote/types.ts (2 hunks)
  • src/kernels/execution/cellExecution.ts (3 hunks)
  • src/kernels/execution/cellExecutionMessageHandler.ts (3 hunks)
  • src/kernels/execution/helpers.ts (9 hunks)
  • src/notebooks/deepnote/deepnoteDataConverter.ts (8 hunks)
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts (3 hunks)
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (1 hunks)
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts (9 hunks)
  • src/notebooks/deepnote/deepnoteNotebookManager.ts (2 hunks)
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (1 hunks)
  • src/notebooks/deepnote/pocket.ts (3 hunks)
  • src/notebooks/deepnote/pocket.unit.test.ts (7 hunks)
  • src/notebooks/serviceRegistry.node.ts (2 hunks)
  • src/notebooks/types.ts (1 hunks)
  • src/platform/logging/types.ts (1 hunks)
  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts (1 hunks)
  • src/webviews/extension-side/dataframe/dataframeController.ts (1 hunks)
  • src/webviews/extension-side/serviceRegistry.node.ts (2 hunks)
  • src/webviews/extension-side/serviceRegistry.web.ts (2 hunks)
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/index.ts (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/tailwind.css (1 hunks)
  • tailwind.config.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.web.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.web.ts for Web-specific implementations using browser-compatible APIs

Files:

  • src/webviews/extension-side/serviceRegistry.web.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • src/webviews/extension-side/serviceRegistry.web.ts
  • src/platform/logging/types.ts
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
  • src/kernels/deepnote/types.ts
  • src/notebooks/types.ts
  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts
  • src/notebooks/serviceRegistry.node.ts
  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/notebooks/deepnote/deepnoteNotebookManager.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/extension.common.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
  • src/kernels/execution/helpers.ts
  • src/kernels/execution/cellExecution.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/webviews/extension-side/serviceRegistry.node.ts
  • src/notebooks/deepnote/pocket.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
  • build/esbuild/build.ts
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/platform/logging/types.ts
  • src/kernels/deepnote/types.ts
  • src/notebooks/types.ts
  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts
  • src/notebooks/deepnote/deepnoteNotebookManager.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/extension.common.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
  • src/kernels/execution/helpers.ts
  • src/kernels/execution/cellExecution.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/notebooks/deepnote/pocket.ts
  • build/esbuild/build.ts
src/platform/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/platform.instructions.md)

src/platform/**/*.ts: Use Inversify decorators for DI: annotate classes with @Injectable() and inject dependencies with @Inject(Token)
Use the centralized logger (import { logger } from '../platform/logging') instead of console.log for application logging

Files:

  • src/platform/logging/types.ts
src/platform/logging/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/platform.instructions.md)

Implement logging using OutputChannelLogger/ConsoleLogger and honor LogLevel; ensure PII scrubbing in log output

Files:

  • src/platform/logging/types.ts
**/*.node.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.node.ts for Desktop-specific implementations that require full file system access and Python environments

Files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
  • src/notebooks/serviceRegistry.node.ts
  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/webviews/extension-side/serviceRegistry.node.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
src/notebooks/deepnote/**

📄 CodeRabbit inference engine (CLAUDE.md)

Deepnote integration code resides under src/notebooks/deepnote/

Files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
  • src/notebooks/deepnote/deepnoteNotebookManager.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/notebooks/deepnote/pocket.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/deepnote/types.ts
  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/kernels/execution/helpers.ts
  • src/kernels/execution/cellExecution.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
**/*.unit.test.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place unit tests in files matching *.unit.test.ts

**/*.unit.test.ts: Unit tests must use Mocha/Chai and have the .unit.test.ts extension
Place unit test files alongside the source files they test
Use assert.deepStrictEqual() for object comparisons in tests instead of checking individual properties

Files:

  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

In unit tests, when a mock is returned from a promise, ensure the mocked instance has an undefined then property to avoid hanging tests

Files:

  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
src/notebooks/deepnote/deepnoteNotebookManager.ts

📄 CodeRabbit inference engine (CLAUDE.md)

deepnoteNotebookManager.ts handles Deepnote notebook state management

Files:

  • src/notebooks/deepnote/deepnoteNotebookManager.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/helpers.ts
  • src/kernels/execution/cellExecution.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
src/notebooks/deepnote/deepnoteDataConverter.ts

📄 CodeRabbit inference engine (CLAUDE.md)

deepnoteDataConverter.ts performs Deepnote data transformations

Files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
🧠 Learnings (24)
📚 Learning: 2025-09-03T13:00:18.307Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/platform.instructions.md:0-0
Timestamp: 2025-09-03T13:00:18.307Z
Learning: Applies to src/platform/**/ioc/types.ts : Define dependency injection tokens as Symbols in types files (e.g., export const IFoo = Symbol('IFoo')) and declare matching TypeScript interfaces

Applied to files:

  • src/platform/logging/types.ts
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use the `ILogger` service instead of `console.log`

Applied to files:

  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
  • src/webviews/webview-side/dataframe-renderer/index.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteActivationService.ts : deepnoteActivationService.ts manages VS Code activation for Deepnote

Applied to files:

  • src/kernels/deepnote/types.ts
  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
📚 Learning: 2025-10-07T19:51:40.201Z
Learnt from: m1so
PR: deepnote/vscode-deepnote#25
File: src/kernels/deepnote/deepnoteServerStarter.node.ts:150-154
Timestamp: 2025-10-07T19:51:40.201Z
Learning: The deepnote-toolkit server is a separate Python package maintained in a different repository from the VS Code extension (vscode-deepnote). The VS Code extension launches the toolkit as a subprocess and communicates via environment variables and HTTP. The toolkit implementation cannot be verified from the extension codebase.

Applied to files:

  • src/kernels/deepnote/types.ts
  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteNotebookManager.ts : deepnoteNotebookManager.ts handles Deepnote notebook state management

Applied to files:

  • src/kernels/deepnote/types.ts
  • src/notebooks/types.ts
  • src/notebooks/deepnote/deepnoteNotebookManager.ts
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to **/*.{ts,tsx} : Use Uri.joinPath() to construct file paths instead of manual string concatenation

Applied to files:

  • CLAUDE.md
📚 Learning: 2025-09-03T12:55:29.175Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/ipywidgets.instructions.md:0-0
Timestamp: 2025-09-03T12:55:29.175Z
Learning: Applies to src/notebooks/controllers/ipywidgets/serviceRegistry.{node,web}.ts : Register DI services in platform-specific registries (serviceRegistry.node.ts vs serviceRegistry.web.ts)

Applied to files:

  • src/notebooks/serviceRegistry.node.ts
📚 Learning: 2025-09-03T12:56:39.535Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel-jupyter.instructions.md:0-0
Timestamp: 2025-09-03T12:56:39.535Z
Learning: Applies to src/kernels/jupyter/serviceRegistry.{node,web}.ts : Use dependency injection via the service registry for component creation and registration

Applied to files:

  • src/notebooks/serviceRegistry.node.ts
📚 Learning: 2025-09-03T13:01:10.017Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/standalone.instructions.md:0-0
Timestamp: 2025-09-03T13:01:10.017Z
Learning: Applies to src/standalone/serviceRegistry.{node,web}.ts : Register new services for both platforms in serviceRegistry.node.ts and serviceRegistry.web.ts as appropriate

Applied to files:

  • src/notebooks/serviceRegistry.node.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : Register the new kernel source in KernelSourceCommandHandler

Applied to files:

  • src/notebooks/serviceRegistry.node.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteTypes.ts : deepnoteTypes.ts contains Deepnote-related type definitions

Applied to files:

  • src/notebooks/serviceRegistry.node.ts
📚 Learning: 2025-09-25T13:16:13.796Z
Learnt from: hc2p
PR: deepnote/environments#62
File: gpu/Dockerfile:0-0
Timestamp: 2025-09-25T13:16:13.796Z
Learning: In Deepnote's Docker environment setup, packages installed in the underlying Docker image should be in system site packages, not in virtual environments. Server/kernels are launched in a virtual environment created by the installer, which creates a pth file where system site packages are appended first, giving them higher precedence and making them first in the search path. This is expected behavior for the deepnote-toolkit setup.

Applied to files:

  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
📚 Learning: 2025-09-25T09:58:15.799Z
Learnt from: hc2p
PR: deepnote/environments#62
File: gpu/Dockerfile:37-38
Timestamp: 2025-09-25T09:58:15.799Z
Learning: In Deepnote environments, the virtual environment must be created at ~/venv path because deepnote-toolkit expects this specific location.

Applied to files:

  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteNotebookSelector.ts : deepnoteNotebookSelector.ts implements UI selection logic for Deepnote notebooks

Applied to files:

  • src/notebooks/deepnote/deepnoteNotebookManager.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Applied to files:

  • src/kernels/execution/cellExecution.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/CellExecutionMessageHandler.ts : CellExecutionMessageHandler must process kernel messages (iopub, execute_reply) and update VS Code cell outputs

Applied to files:

  • src/kernels/execution/cellExecutionMessageHandler.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteDataConverter.ts : deepnoteDataConverter.ts performs Deepnote data transformations

Applied to files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/export/**/*.ts : Update FileConverter to handle the new export format

Applied to files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
📚 Learning: 2025-09-03T12:55:29.175Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/ipywidgets.instructions.md:0-0
Timestamp: 2025-09-03T12:55:29.175Z
Learning: Applies to src/notebooks/controllers/ipywidgets/types.ts : Use the shared core interfaces/types from types.ts across the IPyWidgets controller code

Applied to files:

  • src/webviews/extension-side/serviceRegistry.node.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : For new kernel sources, implement ILocalNotebookKernelSourceSelector (or similar)

Applied to files:

  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/kernelProvider.base.ts : KernelProviderBase must create/manage IKernel instances, track by NotebookDocument, manage lifecycle (start/restart/dispose), and expose INotebookKernelExecution

Applied to files:

  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
📚 Learning: 2025-09-03T12:55:29.175Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/ipywidgets.instructions.md:0-0
Timestamp: 2025-09-03T12:55:29.175Z
Learning: Applies to src/notebooks/controllers/ipywidgets/message/ipyWidgetMessageDispatcher.ts : On kernel restarts, clear pending state, resolve waiting promises, and re-establish hooks and comm targets

Applied to files:

  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
📚 Learning: 2025-09-03T12:56:39.535Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel-jupyter.instructions.md:0-0
Timestamp: 2025-09-03T12:56:39.535Z
Learning: Applies to src/kernels/jupyter/jupyterKernelSpec.ts : Manage kernel spec registration and updates; create temporary kernel specs for non-standard kernels when needed

Applied to files:

  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
📚 Learning: 2025-09-03T12:55:29.175Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/ipywidgets.instructions.md:0-0
Timestamp: 2025-09-03T12:55:29.175Z
Learning: Applies to src/notebooks/controllers/notebookIPyWidgetCoordinator.ts : NotebookIPyWidgetCoordinator should manage per-notebook lifecycle, controller selection, webview attachment, and creation of CommonMessageCoordinator instances

Applied to files:

  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
🧬 Code graph analysis (17)
src/webviews/extension-side/serviceRegistry.web.ts (1)
src/platform/activation/types.ts (2)
  • IExtensionSyncActivationService (15-15)
  • IExtensionSyncActivationService (22-24)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (3)
gulpfile.js (1)
  • key (160-160)
src/test/datascience/ipywidgets/samples/jupyter-require/core.js (1)
  • context (348-351)
src/notebooks/controllers/vscodeNotebookController.ts (1)
  • label (113-115)
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (2)
src/platform/logging/types.ts (2)
  • ILogger (9-9)
  • ILogger (10-19)
src/test/mocks/vsc/index.ts (1)
  • CancellationToken (100-112)
src/standalone/intellisense/resolveCompletionItem.unit.test.ts (1)
src/kernels/execution/helpers.ts (1)
  • cellOutputToVSCCellOutput (132-170)
src/notebooks/serviceRegistry.node.ts (2)
src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (2)
  • IDeepnoteInitNotebookRunner (283-283)
  • IDeepnoteInitNotebookRunner (284-286)
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (2)
  • IDeepnoteRequirementsHelper (142-142)
  • IDeepnoteRequirementsHelper (143-145)
src/kernels/deepnote/deepnoteToolkitInstaller.node.ts (2)
src/kernels/deepnote/types.ts (1)
  • DEEPNOTE_TOOLKIT_VERSION (157-157)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/webviews/webview-side/dataframe-renderer/index.ts (2)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (2)
  • DataframeMetadata (6-8)
  • DataframeRenderer (39-192)
src/webviews/extension-side/dataframe/dataframeController.ts (1)
  • activate (53-56)
src/notebooks/deepnote/pocket.unit.test.ts (1)
src/notebooks/deepnote/pocket.ts (2)
  • extractPocketFromCellMetadata (44-46)
  • createBlockFromPocket (48-84)
src/extension.common.ts (2)
src/platform/logging/types.ts (2)
  • ILogger (9-9)
  • ILogger (10-19)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/kernels/execution/helpers.ts (1)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • NotebookCellOutput (71-103)
  • NotebookCellOutputItem (16-69)
src/kernels/execution/cellExecution.ts (2)
src/platform/logging/index.ts (1)
  • logger (35-48)
build/webTestReporter.js (1)
  • dedent (9-9)
src/kernels/execution/cellExecutionMessageHandler.ts (2)
src/kernels/execution/helpers.ts (1)
  • cellOutputToVSCCellOutput (132-170)
src/platform/common/utils.ts (2)
  • concatMultilineString (211-225)
  • formatStreamText (275-278)
src/webviews/extension-side/dataframe/dataframeController.ts (3)
src/platform/common/types.ts (1)
  • IDisposable (211-213)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • WorkspaceEdit (649-833)
  • NotebookEdit (2444-2507)
src/notebooks/deepnote/deepnoteDataConverter.ts (2)
src/notebooks/deepnote/deepnoteTypes.ts (1)
  • DeepnoteOutput (23-51)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • NotebookCellOutput (71-103)
  • NotebookCellOutputItem (16-69)
src/webviews/extension-side/serviceRegistry.node.ts (1)
src/platform/activation/types.ts (2)
  • IExtensionSyncActivationService (15-15)
  • IExtensionSyncActivationService (22-24)
src/notebooks/deepnote/pocket.ts (1)
src/notebooks/deepnote/dataConversionUtils.ts (1)
  • generateBlockId (8-15)
src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts (6)
src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (2)
  • IDeepnoteInitNotebookRunner (283-283)
  • IDeepnoteInitNotebookRunner (284-286)
src/notebooks/types.ts (2)
  • IDeepnoteNotebookManager (27-27)
  • IDeepnoteNotebookManager (28-37)
src/kernels/types.ts (3)
  • IKernelProvider (548-548)
  • IKernelProvider (549-564)
  • IKernel (439-446)
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (2)
  • IDeepnoteRequirementsHelper (142-142)
  • IDeepnoteRequirementsHelper (143-145)
src/kernels/deepnote/types.ts (1)
  • DEEPNOTE_NOTEBOOK_TYPE (162-162)
src/test/mocks/vsc/index.ts (1)
  • CancellationTokenSource (114-125)
🪛 Biome (2.1.2)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx

[error] 153-164: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)


[error] 170-181: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

🪛 ESLint
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx

[error] 19-19: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)


[error] 25-25: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🪛 markdownlint-cli2 (0.18.1)
DEEPNOTE_KERNEL_IMPLEMENTATION.md

371-371: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


372-372: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


377-377: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


378-378: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


384-384: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


385-385: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


390-390: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


391-391: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


397-397: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


398-398: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build & Test
🔇 Additional comments (39)
CLAUDE.md (1)

4-4: LGTM.

Clear guidance on platform-correct path construction.

Based on learnings.

src/standalone/intellisense/resolveCompletionItem.unit.test.ts (1)

549-549: LGTM.

Explicit wrapper enables future parameter passing (cellIndex, cellId) as cellOutputToVSCCellOutput signature evolved.

src/webviews/extension-side/serviceRegistry.node.ts (1)

20-20: LGTM.

Standard DI registration for DataframeController follows established patterns.

Also applies to: 69-69

src/webviews/webview-side/dataframe-renderer/tailwind.css (1)

1-3: LGTM.

Standard Tailwind CSS directives.

src/notebooks/serviceRegistry.node.ts (1)

56-57: LGTM.

DI registrations for Deepnote init-notebook services follow established patterns.

Also applies to: 140-141

postcss.config.js (1)

1-6: LGTM.

Standard PostCSS configuration with Tailwind v4 and autoprefixer.

Based on learnings.

README.md (1)

12-13: LGTM.

Documentation accurately reflects new init-notebook and dependency management capabilities.

src/extension.common.ts (2)

40-40: LGTM.

Import follows DI token pattern.


131-131: LGTM.

Singleton registration follows established pattern and enables DI-based logging access.

src/platform/logging/types.ts (1)

9-9: LGTM.

Symbol export follows DI token pattern. Based on learnings.

src/notebooks/deepnote/pocket.ts (4)

6-8: LGTM.

Comments clearly document the intentional exclusion of id from pocket fields.


11-18: LGTM.

Interface update consistent with field list change.


52-59: LGTM.

Extraction and cleanup logic properly handles id migration from metadata to block.id.


69-69: LGTM.

Fallback ensures block always has an id.

src/notebooks/types.ts (1)

35-36: LGTM.

Interface extension supports per-project init-notebook tracking with clear method signatures.

src/notebooks/deepnote/deepnoteNotebookManager.ts (3)

15-15: LGTM.

Set provides efficient tracking of init-notebook execution state per project.


78-85: LGTM.

Method correctly implements interface contract with clear documentation.


87-93: LGTM.

Method correctly implements interface contract with clear documentation.

src/webviews/extension-side/serviceRegistry.web.ts (2)

30-30: LGTM.

Import follows established pattern.


69-69: LGTM.

Registration follows established activation service pattern.

src/notebooks/deepnote/deepnoteDataConverter.ts (7)

31-31: LGTM.

Per-block indexing enables cellIndex/cellId metadata propagation.

Also applies to: 57-57


205-209: LGTM.

Signature extension supports metadata propagation with clear parameter names.


220-225: LGTM.

Metadata propagation for error outputs is consistent and clear.


286-299: LGTM.

Metadata handling properly preserves existing fields while adding cell identity.


312-320: LGTM.

Stream output metadata consistent with other output types.


325-333: LGTM.

Unknown output metadata handling consistent with known output types.


342-350: LGTM.

Fallback output metadata completes consistent propagation across all output paths.

src/kernels/execution/cellExecution.ts (3)

35-35: LGTM.

Import supports clean formatting of prepended Python code.


532-538: LGTM.

Escape order is correct (backslashes first) and sufficient for embedding JSON strings.


410-434: LGTM.

Table state injection is well-structured with proper logging and safe string escaping.

src/webviews/extension-side/dataframe/dataframeController.ts (1)

1-1: Add the standard header.

All new TypeScript sources need the Microsoft copyright/license header; please add it.

⛔ Skipped due to learnings
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to **/*.{ts,tsx} : Do not add the Microsoft copyright header to new files
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Use dependency injection with inversify
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/platform.instructions.md:0-0
Timestamp: 2025-09-03T13:00:18.307Z
Learning: Applies to src/platform/**/*.ts : Use Inversify decorators for DI: annotate classes with injectable() and inject dependencies with inject(Token)
src/kernels/execution/helpers.ts (7)

118-121: LGTM: Mapper signatures accept cell provenance

Map now carries (cellIndex, cellId) through. Good.


132-168: LGTM: Propagation in cellOutputToVSCCellOutput

Optional args forwarded; sensible fallback to display_data.


172-185: LGTM: Metadata enriched with cellIndex/cellId

Non-invasive; fields are optional and won’t leak into ipynb save path.


219-222: LGTM: Display translator now includes provenance metadata

Forwarding through getOutputMetadata looks correct.


256-260: LGTM: Stream translator uses enriched metadata

No behavior change; adds provenance.


303-311: LGTM: CellOutputMetadata extended

Fields are optional; no breaking change.


571-582: LGTM: Error translator attaches originalError with provenance

Good for diagnostics; maintains existing shape.

src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts (1)

401-409: Kernel spec selection: good; keep hash in one place

Using getVenvHash aligns selector with installer. Nice.

Comment on lines 149 to 175
private async handleGoToPage(editor: NotebookEditor, message: GoToPageCommand) {
let cell;
let cellIndex: number;

// Try to find cell by cellId first (more reliable)
if (message.cellId) {
const cells = editor.notebook.getCells();
const foundCell = cells.find((c) => c.metadata.id === message.cellId);

if (foundCell) {
cell = foundCell;
cellIndex = foundCell.index;
logger.info(`[DataframeController] Found cell by cellId ${message.cellId} at index ${cellIndex}`);
} else {
const errorMessage = `Unable to navigate to page: Could not find the cell with ID ${message.cellId}. The cell may have been deleted.`;
logger.error(`[DataframeController] ${errorMessage}`);
await window.showErrorMessage(errorMessage);
throw new Error(errorMessage);
}
} else {
const errorMessage =
'Unable to navigate to page: No cell identifier provided. ' +
'Please re-run the cell to update the output metadata.';
logger.error(`[DataframeController] ${errorMessage}`);
await window.showErrorMessage(errorMessage);
throw new Error(errorMessage);
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore the cellIndex fallback.

goToPage errors whenever the renderer only supplies cellIndex (common before metadata includes id). handleSelectPageSize still supports that path; goToPage should mirror it to avoid breaking paging on those cells.

🤖 Prompt for AI Agents
In src/webviews/extension-side/dataframe/dataframeController.ts around lines 149
to 175, the current logic throws if message.cellId is missing or not found, but
the renderer may supply only message.cellIndex; restore the fallback so
handleGoToPage also accepts and uses message.cellIndex when cellId is absent or
lookup by id fails. Specifically: if message.cellId lookup fails, check for a
valid numeric message.cellIndex within editor.notebook.cellCount and select that
cell (set cell and cellIndex) instead of erroring; likewise if message.cellId is
undefined initially, use message.cellIndex when present; ensure you log which
path was used (found by id or by index) and only show/throw an error if neither
id nor a valid index is available.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to throw an error if there is no cell with the given ID.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Artmann, understood—I've noted that handleGoToPage should strictly require a valid cellId and throw if it's missing or not found. I'll keep that in mind for future reviews.

// Including the version ensures a new venv is created when the toolkit version changes
const hash = this.getVenvHash(deepnoteFileUri);
return Uri.joinPath(this.context.globalStorageUri, 'deepnote-venvs', hash);
return Uri.joinPath(this.context.globalStorageUri, 'deepnote-venvs', `${hash}-${DEEPNOTE_TOOLKIT_VERSION}`);
Copy link
Member Author

@Artmann Artmann Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This creates a new venv if there is a new version of the Deepnote Toolkit.

@Artmann Artmann force-pushed the chris/display-data-frames branch from 930dcde to 1ba9ab4 Compare October 13, 2025 12:52
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/kernels/deepnote/deepnoteToolkitInstaller.node.ts (3)

133-146: Don’t nuke existing venvs unless they’re broken.

Delete only if the interpreter is missing; otherwise skip re‑creating and proceed to pip installs. This avoids unnecessary churn and speeds upgrades.

Apply:

-            // Remove old venv if it exists but is broken
-            if (await this.fs.exists(venvPath)) {
-                logger.info('Removing existing broken venv');
-                await workspace.fs.delete(venvPath, { recursive: true });
-            }
+            // Remove old venv only if broken (no interpreter inside it)
+            const pythonInVenv =
+                process.platform === 'win32'
+                    ? Uri.joinPath(venvPath, 'Scripts', 'python.exe')
+                    : Uri.joinPath(venvPath, 'bin', 'python');
+            if (await this.fs.exists(venvPath)) {
+                if (!(await this.fs.exists(pythonInVenv))) {
+                    logger.info('Removing existing broken venv');
+                    await workspace.fs.delete(venvPath, { recursive: true });
+                } else {
+                    logger.info('Using existing venv');
+                }
+            }
@@
-            const venvResult = await processService.exec(baseInterpreter.uri.fsPath, ['-m', 'venv', venvPath.fsPath], {
-                throwOnStdErr: false
-            });
+            let venvResult = { stderr: '' } as { stderr?: string };
+            if (!(await this.fs.exists(pythonInVenv))) {
+                venvResult = await processService.exec(
+                    baseInterpreter.uri.fsPath,
+                    ['-m', 'venv', venvPath.fsPath],
+                    { throwOnStdErr: false }
+                );
+            }

248-250: Cache interpreter on success.

Set the cache so subsequent lookups are O(1).

-                this.outputChannel.appendLine('✓ Deepnote toolkit ready');
-                return venvInterpreter;
+                this.outputChannel.appendLine('✓ Deepnote toolkit ready');
+                this.venvPythonPaths.set(venvPath.fsPath, venvInterpreter.uri);
+                return venvInterpreter;

295-299: Windows-safe display name.

Split on path separators via uri utils instead of '/'.

-        const parts = deepnoteFileUri.fsPath.split('/');
-        return parts[parts.length - 1] || 'notebook';
+        // Use vscode-path helpers for cross-platform basenames
+        return (require('../../platform/vscode-path/resources') as typeof import('../../platform/vscode-path/resources'))
+            .basename(deepnoteFileUri) || 'notebook';

Also add at top-level imports:

+// import * as uriPath from '../../platform/vscode-path/resources';
src/kernels/execution/cellExecutionMessageHandler.ts (1)

989-999: Stream append targets the wrong output.

You check the last output’s items, but then store outputs[0] as the target. This can append to the first output incorrectly.

Apply:

-                this.lastUsedStreamOutput = {
-                    output: this.cell.outputs[0],
-                    stream: msg.content.name
-                };
+                this.lastUsedStreamOutput = {
+                    output: this.cell.outputs[this.cell.outputs.length - 1],
+                    stream: msg.content.name
+                };
src/notebooks/deepnote/pocket.ts (1)

66-73: Fallback block type should reflect cell.kind.

Defaulting to 'code' can misclassify markup cells lacking type in pocket.

Apply:

-    const block: DeepnoteBlock = {
+    const block: DeepnoteBlock = {
         blockGroup: pocket?.blockGroup || 'default-group',
         content: cell.value,
-        id: cellId || generateBlockId(),
+        id: cellId || generateBlockId(),
         metadata,
-        sortingKey: pocket?.sortingKey || generateSortingKey(index),
-        type: pocket?.type || 'code'
+        sortingKey: pocket?.sortingKey || generateSortingKey(index),
+        type: pocket?.type ?? ((cell as any).kind === NotebookCellKind.Code ? 'code' : 'markdown')
     };

And add import:

-import type { NotebookCellData } from 'vscode';
+import type { NotebookCellData } from 'vscode';
+import { NotebookCellKind } from 'vscode';
♻️ Duplicate comments (5)
build/esbuild/build.ts (1)

320-324: Use watchAll for consistency.

Same ask as before: align with other webview builds so watch‑all rebuilds this as well.

-            { target: 'web', watch: isWatchMode }
+            { target: 'web', watch: watchAll }
src/webviews/webview-side/dataframe-renderer/index.ts (1)

39-61: Add React cleanup to prevent memory leaks.

ReactDOM.render mounts components but disposeOutputItem never unmounts them, causing memory leaks when outputs are disposed.

Store root elements and call ReactDOM.unmountComponentAtNode(root) in disposeOutputItem:

 export const activate: ActivationFunction = (context: RendererContext<unknown>) => {
+    const roots = new Map<string, HTMLElement>();
+
     return {
         renderOutputItem(outputItem: OutputItem, element: HTMLElement) {
             try {
                 const data = outputItem.json();
                 console.log('Dataframe renderer - received data:', data);
 
                 const metadata = outputItem.metadata as Metadata | undefined;
                 console.log('[DataframeRenderer] Full metadata:', metadata);
                 console.log('[DataframeRenderer] Full outputItem:', outputItem);
 
                 const dataFrameMetadata = metadata?.metadata as DataframeMetadata | undefined;
                 const cellId = metadata?.cellId;
                 const cellIndex = metadata?.cellIndex;
 
                 console.log('[DataframeRenderer] Extracted cellId:', cellId, 'cellIndex:', cellIndex);
 
                 const root = document.createElement('div');
                 element.appendChild(root);
+                roots.set(outputItem.id, root);
 
                 ReactDOM.render(
                     React.createElement(DataframeRenderer, {
                         context,
                         data,
                         metadata: dataFrameMetadata,
                         cellId,
                         cellIndex
                     }),
                     root
                 );
             } catch (error) {
                 console.error('Error rendering dataframe:', error);
                 const errorDiv = document.createElement('div');
                 errorDiv.style.padding = '10px';
                 errorDiv.style.color = 'var(--vscode-errorForeground)';
                 errorDiv.textContent = `Error rendering dataframe: ${error}`;
                 element.appendChild(errorDiv);
             }
         },
 
-        disposeOutputItem(_id?: string) {
-            // Cleanup if needed
+        disposeOutputItem(id?: string) {
+            if (id) {
+                const root = roots.get(id);
+                if (root) {
+                    ReactDOM.unmountComponentAtNode(root);
+                    roots.delete(id);
+                }
+            }
         }
     };
 };
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (2)

46-92: Replace console logging with ILogger usage or remove the calls.

Per guidelines we must not ship console.log in TSX. Please route these messages through the ILogger plumbing (or drop them if unnecessary). Based on learnings.

-    console.log('[DataframeRenderer] Rendering with:', { cellId, cellIndex, data, metadata });
...
-        console.log('[DataframeRenderer] handlePageSizeChange called with cellId:', cellId, 'cellIndex:', cellIndex);
...
-        console.log('[DataframeRenderer] Posting message:', message);
...
-        console.log('[DataframeRenderer] handlePageChange called with cellId:', cellId, 'page:', newPageIndex);
...
-        console.log('[DataframeRenderer] Posting message:', message);

14-27: Eliminate any in column/row typings.

any breaks our lint gate. Please tighten these shapes (e.g. Record<string, unknown> or a proper interface). Based on learnings.

-        columns: {
-            dtype: string;
-            name: string;
-            stats: any;
-        }[];
+        columns: {
+            dtype: string;
+            name: string;
+            stats: Record<string, unknown>;
+        }[];
...
-        rows: {
-            _deepnote_index_column: number;
-            [key: string]: any;
-        }[];
+        rows: Array<
+            {
+                _deepnote_index_column: number;
+            } & Record<string, unknown>
+        >;
src/webviews/extension-side/dataframe/dataframeController.ts (1)

149-175: Restore the cellIndex fallback when cellId is absent.

Without the index path, paging fails for outputs that still only expose cellIndex. Mirror handleSelectPageSize.

         if (message.cellId) {
             const cells = editor.notebook.getCells();
             const foundCell = cells.find((c) => c.metadata.id === message.cellId);
 
             if (foundCell) {
                 cell = foundCell;
                 cellIndex = foundCell.index;
                 logger.info(`[DataframeController] Found cell by cellId ${message.cellId} at index ${cellIndex}`);
             } else {
                 const errorMessage = `Unable to navigate to page: Could not find the cell with ID ${message.cellId}. The cell may have been deleted.`;
                 logger.error(`[DataframeController] ${errorMessage}`);
                 await window.showErrorMessage(errorMessage);
                 throw new Error(errorMessage);
             }
-        } else {
+        } else if (message.cellIndex !== undefined) {
+            try {
+                cell = editor.notebook.cellAt(message.cellIndex);
+                cellIndex = message.cellIndex;
+                logger.info(`[DataframeController] Using cellIndex ${cellIndex} (cellId not available)`);
+            } catch (error) {
+                const errorMessage = `Unable to navigate to page: Cell at index ${message.cellIndex} not found. The notebook structure may have changed.`;
+                logger.error(`[DataframeController] ${errorMessage}`, error);
+                await window.showErrorMessage(errorMessage);
+                throw new Error(errorMessage);
+            }
+        } else {
             const errorMessage =
                 'Unable to navigate to page: No cell identifier provided. ' +
                 'Please re-run the cell to update the output metadata.';
             logger.error(`[DataframeController] ${errorMessage}`);
             await window.showErrorMessage(errorMessage);
             throw new Error(errorMessage);
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f93fb09 and 1ba9ab4.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (22)
  • build/esbuild/build.ts (5 hunks)
  • package.json (5 hunks)
  • postcss.config.js (1 hunks)
  • src/kernels/deepnote/deepnoteController.ts (1 hunks)
  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts (2 hunks)
  • src/kernels/deepnote/types.ts (1 hunks)
  • src/kernels/execution/cellExecution.ts (2 hunks)
  • src/kernels/execution/cellExecutionMessageHandler.ts (3 hunks)
  • src/kernels/execution/helpers.ts (9 hunks)
  • src/notebooks/controllers/vscodeNotebookController.ts (3 hunks)
  • src/notebooks/deepnote/deepnoteDataConverter.ts (8 hunks)
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts (3 hunks)
  • src/notebooks/deepnote/pocket.ts (3 hunks)
  • src/notebooks/deepnote/pocket.unit.test.ts (7 hunks)
  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts (1 hunks)
  • src/webviews/extension-side/dataframe/dataframeController.ts (1 hunks)
  • src/webviews/extension-side/serviceRegistry.node.ts (2 hunks)
  • src/webviews/extension-side/serviceRegistry.web.ts (2 hunks)
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/index.ts (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/tailwind.css (1 hunks)
  • tailwind.config.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • build/esbuild/build.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/kernels/deepnote/types.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/kernels/execution/cellExecution.ts
  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/kernels/execution/helpers.ts
  • src/kernels/deepnote/deepnoteController.ts
  • src/notebooks/deepnote/pocket.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • build/esbuild/build.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/kernels/deepnote/types.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/kernels/execution/cellExecution.ts
  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/webviews/extension-side/serviceRegistry.node.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/kernels/execution/helpers.ts
  • src/webviews/extension-side/serviceRegistry.web.ts
  • src/kernels/deepnote/deepnoteController.ts
  • src/notebooks/deepnote/pocket.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
src/notebooks/deepnote/**

📄 CodeRabbit inference engine (CLAUDE.md)

Deepnote integration code resides under src/notebooks/deepnote/

Files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/notebooks/deepnote/pocket.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
src/notebooks/deepnote/deepnoteDataConverter.ts

📄 CodeRabbit inference engine (CLAUDE.md)

deepnoteDataConverter.ts performs Deepnote data transformations

Files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
**/*.node.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.node.ts for Desktop-specific implementations that require full file system access and Python environments

Files:

  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/webviews/extension-side/serviceRegistry.node.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/kernels/deepnote/types.ts
  • src/kernels/execution/cellExecution.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/kernels/execution/helpers.ts
  • src/kernels/deepnote/deepnoteController.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecution.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/kernels/execution/helpers.ts
**/*.unit.test.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place unit tests in files matching *.unit.test.ts

**/*.unit.test.ts: Unit tests must use Mocha/Chai and have the .unit.test.ts extension
Place unit test files alongside the source files they test
Use assert.deepStrictEqual() for object comparisons in tests instead of checking individual properties

Files:

  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

In unit tests, when a mock is returned from a promise, ensure the mocked instance has an undefined then property to avoid hanging tests

Files:

  • src/standalone/intellisense/resolveCompletionItem.unit.test.ts
  • src/notebooks/deepnote/pocket.unit.test.ts
  • src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts
**/*.web.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.web.ts for Web-specific implementations using browser-compatible APIs

Files:

  • src/webviews/extension-side/serviceRegistry.web.ts
🧠 Learnings (14)
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteDataConverter.ts : deepnoteDataConverter.ts performs Deepnote data transformations

Applied to files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/export/**/*.ts : Update FileConverter to handle the new export format

Applied to files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
📚 Learning: 2025-09-25T09:58:15.799Z
Learnt from: hc2p
PR: deepnote/environments#62
File: gpu/Dockerfile:37-38
Timestamp: 2025-09-25T09:58:15.799Z
Learning: In Deepnote environments, the virtual environment must be created at ~/venv path because deepnote-toolkit expects this specific location.

Applied to files:

  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteActivationService.ts : deepnoteActivationService.ts manages VS Code activation for Deepnote

Applied to files:

  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/kernels/deepnote/types.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
📚 Learning: 2025-10-07T19:51:40.201Z
Learnt from: m1so
PR: deepnote/vscode-deepnote#25
File: src/kernels/deepnote/deepnoteServerStarter.node.ts:150-154
Timestamp: 2025-10-07T19:51:40.201Z
Learning: The deepnote-toolkit server is a separate Python package maintained in a different repository from the VS Code extension (vscode-deepnote). The VS Code extension launches the toolkit as a subprocess and communicates via environment variables and HTTP. The toolkit implementation cannot be verified from the extension codebase.

Applied to files:

  • src/kernels/deepnote/deepnoteToolkitInstaller.node.ts
  • src/kernels/deepnote/types.ts
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use the `ILogger` service instead of `console.log`

Applied to files:

  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteNotebookManager.ts : deepnoteNotebookManager.ts handles Deepnote notebook state management

Applied to files:

  • src/kernels/deepnote/types.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Applied to files:

  • src/kernels/execution/cellExecution.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/kernels/deepnote/deepnoteController.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : For new kernel sources, implement ILocalNotebookKernelSourceSelector (or similar)

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : Create a UI provider for kernel selection for the new kernel source

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:54:35.368Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/interactiveWindow.instructions.md:0-0
Timestamp: 2025-09-03T12:54:35.368Z
Learning: Applies to src/interactive-window/interactiveWindowController.ts : InteractiveWindowController must manage kernel selection/startup/lifecycle, system info cell handling, restart logic, and maintain owner file associations for kernel context

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/kernelProvider.base.ts : KernelProviderBase must create/manage IKernel instances, track by NotebookDocument, manage lifecycle (start/restart/dispose), and expose INotebookKernelExecution

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:55:29.175Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/ipywidgets.instructions.md:0-0
Timestamp: 2025-09-03T12:55:29.175Z
Learning: Applies to src/notebooks/controllers/ipywidgets/types.ts : Use the shared core interfaces/types from types.ts across the IPyWidgets controller code

Applied to files:

  • src/webviews/extension-side/serviceRegistry.node.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/CellExecutionMessageHandler.ts : CellExecutionMessageHandler must process kernel messages (iopub, execute_reply) and update VS Code cell outputs

Applied to files:

  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/kernels/execution/helpers.ts
🧬 Code graph analysis (13)
build/esbuild/build.ts (1)
build/webpack/common.js (1)
  • path (6-6)
src/notebooks/deepnote/deepnoteDataConverter.ts (2)
src/notebooks/deepnote/deepnoteTypes.ts (1)
  • DeepnoteOutput (23-51)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • NotebookCellOutput (71-103)
  • NotebookCellOutputItem (16-69)
src/kernels/deepnote/deepnoteToolkitInstaller.node.ts (1)
src/kernels/deepnote/types.ts (1)
  • DEEPNOTE_TOOLKIT_VERSION (157-157)
src/webviews/extension-side/dataframe/dataframeController.ts (2)
src/platform/common/types.ts (1)
  • IDisposable (211-213)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • WorkspaceEdit (649-833)
  • NotebookEdit (2444-2507)
src/kernels/execution/cellExecution.ts (1)
src/notebooks/deepnote/pocket.ts (1)
  • createBlockFromPocket (48-84)
src/standalone/intellisense/resolveCompletionItem.unit.test.ts (1)
src/kernels/execution/helpers.ts (1)
  • cellOutputToVSCCellOutput (132-170)
src/notebooks/deepnote/pocket.unit.test.ts (1)
src/notebooks/deepnote/pocket.ts (2)
  • extractPocketFromCellMetadata (44-46)
  • createBlockFromPocket (48-84)
src/notebooks/controllers/vscodeNotebookController.ts (2)
src/kernels/deepnote/deepnoteController.ts (1)
  • DeepnoteController (78-84)
src/kernels/types.ts (1)
  • IKernelController (930-933)
src/webviews/extension-side/serviceRegistry.node.ts (1)
src/platform/activation/types.ts (2)
  • IExtensionSyncActivationService (15-15)
  • IExtensionSyncActivationService (22-24)
src/kernels/execution/cellExecutionMessageHandler.ts (2)
src/kernels/execution/helpers.ts (1)
  • cellOutputToVSCCellOutput (132-170)
src/platform/common/utils.ts (2)
  • concatMultilineString (211-225)
  • formatStreamText (275-278)
src/webviews/extension-side/serviceRegistry.web.ts (1)
src/platform/activation/types.ts (2)
  • IExtensionSyncActivationService (15-15)
  • IExtensionSyncActivationService (22-24)
src/kernels/deepnote/deepnoteController.ts (1)
src/kernels/kernelController.ts (1)
  • KernelController (7-15)
src/notebooks/deepnote/pocket.ts (1)
src/notebooks/deepnote/dataConversionUtils.ts (1)
  • generateBlockId (8-15)
🪛 Biome (2.1.2)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx

[error] 153-164: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)


[error] 170-181: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

🔇 Additional comments (17)
src/kernels/deepnote/deepnoteToolkitInstaller.node.ts (2)

31-37: Version-scoped venvs: good; confirm discovery path.

Including DEEPNOTE_TOOLKIT_VERSION in the venv path is the right call to auto‑reprovision on upgrades. Please confirm the toolkit doesn’t assume a fixed ~/venv path at runtime; if it does, we need a symlink or alternate discovery. Based on learnings.


141-175: Long-running execs lack timeouts/cancellation wiring.

Consider passing a timeout and/or hooking the CancellationToken into exec so we can kill stuck venv/pip processes. If the process service supports it, thread token/timeout through these exec calls.

Also applies to: 167-201

src/kernels/execution/helpers.ts (1)

118-121: Consistent per‑cell provenance propagation.

Signatures and metadata wiring look correct; cellIndex/cellId stay in VS Code metadata and aren’t serialized into nbformat. LGTM.

Also applies to: 132-169, 172-186, 218-254, 256-260, 301-321, 571-582

build/esbuild/build.ts (1)

90-97: Tailwind/PostCSS integration: looks good.

Scoped processing to dataframe renderer and keeps other CSS paths unchanged. LGTM.

Also applies to: 141-161, 189-192

src/standalone/intellisense/resolveCompletionItem.unit.test.ts (1)

549-549: Avoid passing map’s index/array to cellOutputToVSCCellOutput
Wrapping with an explicit arrow function ensures only the output is passed as (cellOutput); map’s index/array won’t be treated as (cellIndex, cellId). No other direct .map(cellOutputToVSCCellOutput) usages found.

src/webviews/webview-side/dataframe-renderer/tailwind.css (1)

1-3: Tailwind entry looks good.

Directives are correct and scoped via config.

src/notebooks/deepnote/pocket.ts (1)

6-12: Good: keep id at top-level metadata, not in pocket.

This avoids pocket churn and eases runtime access.

tailwind.config.js (1)

1-24: Tailwind config looks solid and webview-safe.

Scoped content, VS Code tokens, and preflight disabled are appropriate.

src/notebooks/deepnote/deepnoteDataConverter.ts (1)

31-31: Good: propagate cellIndex/cellId into outputs.

Consistent metadata across all output types enables downstream routing.

Also applies to: 57-58

postcss.config.js (1)

1-6: Approve PostCSS plugin configuration Tailwind v4+ (^4.1.14) and '@tailwindcss/postcss' (^4.1.14) are installed.

src/kernels/deepnote/types.ts (1)

89-95: Verify tests and consumers of IDeepnoteToolkitInstaller support new getVenvHash()
DeepnoteToolkitInstaller implements getVenvHash; verify tests and any callers of IDeepnoteToolkitInstaller handle the added method.

src/webviews/extension-side/serviceRegistry.node.ts (1)

20-20: Registration and activate() for DataframeController verified
Web registry mirrors the node registry and DataframeController implements activate().

src/kernels/execution/cellExecution.ts (1)

411-423: LGTM!

The Deepnote block conversion and code generation logic is correctly implemented. Cell data is properly assembled, converted to a block via createBlockFromPocket, and code is generated using createPythonCode.

src/notebooks/deepnote/deepnoteDataConverter.unit.test.ts (1)

1-521: LGTM!

Test updates correctly reflect the refactored metadata structure where id lives at top-level instead of inside __deepnotePocket. All test cases are consistent with this change.

src/notebooks/controllers/vscodeNotebookController.ts (1)

551-551: LGTM!

DeepnoteController integration is correct. The controller is properly instantiated for cell execution creation.

Also applies to: 564-564

src/notebooks/deepnote/pocket.unit.test.ts (1)

1-209: LGTM!

Test updates correctly reflect the refactored pocket structure where id is at top-level metadata rather than inside __deepnotePocket. All assertions are consistent.

package.json (1)

1812-1822: LGTM!

The dataframe renderer contribution is properly configured with the correct entrypoint, mime type, and messaging support.

Comment on lines 157 to 161
export const DEEPNOTE_TOOLKIT_VERSION = '0.2.30.dev29+890433e';
export const DEEPNOTE_TOOLKIT_WHEEL_URL = `https://deepnote-staging-runtime-artifactory.s3.amazonaws.com/deepnote-toolkit-packages/${encodeURIComponent(
DEEPNOTE_TOOLKIT_VERSION
)}/deepnote_toolkit-${encodeURIComponent(DEEPNOTE_TOOLKIT_VERSION)}-py3-none-any.whl`;
export const DEEPNOTE_DEFAULT_PORT = 8888;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid hardcoding dev version and staging URL in main.

Using a dev build (0.2.30.dev29+890433e) and a staging S3 bucket can break production installs. Gate behind a feature flag or revert to a stable, public wheel before merging.

Consider:

  • Read version/channel from settings or env, defaulting to a stable release.
  • Parameterize base URL to avoid baking staging endpoints into main.
🤖 Prompt for AI Agents
In src/kernels/deepnote/types.ts around lines 157 to 161, the code hardcodes a
dev toolkit version and a staging S3 wheel URL; replace this by reading the
toolkit version/channel and the base wheel URL from configuration or environment
(with sensible defaults to a stable public release and public URL), and gate use
of non-stable/staging values behind a feature flag or explicit env variable.
Update DEEPNOTE_TOOLKIT_VERSION to be derived from settings/env (fallback to a
stable release), make DEEPNOTE_TOOLKIT_WHEEL_URL compose from a configurable
base URL plus the encoded version, and ensure any staging-only behavior requires
an enabled feature flag to avoid leaking staging endpoints into production.

Comment on lines 205 to 209
private transformOutputsForVsCode(
outputs: DeepnoteOutput[],
cellIndex: number,
cellId: string
): NotebookCellOutput[] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Make base64 conversion cross-runtime (Node + Web).

base64ToUint8Array uses atob (web API). In Node, prefer Buffer. Update the helper to support both.

Apply outside this hunk:

// Replace existing helper
private base64ToUint8Array(base64: string): Uint8Array {
    // Browser path
    if (typeof atob === 'function') {
        const binaryString = atob(base64);
        const bytes = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
            bytes[i] = binaryString.charCodeAt(i);
        }
        return bytes;
    }
    // Node path
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const Buf: any = (globalThis as any).Buffer;
    if (Buf && typeof Buf.from === 'function') {
        return new Uint8Array(Buf.from(base64, 'base64'));
    }
    // Fallback: safe empty
    return new Uint8Array();
}
🤖 Prompt for AI Agents
In src/notebooks/deepnote/deepnoteDataConverter.ts around lines 205 to 209, the
helper that converts base64 to Uint8Array uses atob which only works in
browsers; update or replace the helper so it detects runtime and supports both
Web (use atob) and Node (use globalThis.Buffer.from) and returns a Uint8Array,
with a safe empty Uint8Array fallback if neither is available; ensure the new
helper is used by transformOutputsForVsCode and keep types compatible with
TypeScript (avoid direct any if possible).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (2)

55-61: Multi‑root: avoid always using the first workspace

Select the correct workspace (active editor’s folder if available), not index 0.

-            const workspaceFolders = workspace.workspaceFolders;
-            if (!workspaceFolders || workspaceFolders.length === 0) {
+            const workspaceFolders = workspace.workspaceFolders;
+            if (!workspaceFolders || workspaceFolders.length === 0) {
                 this.logger.info('No workspace folder found, cannot create requirements.txt');
                 return;
             }
+            // Prefer the active editor's workspace folder when available
+            const activeUri = window.activeTextEditor?.document.uri;
+            const workspaceFolder =
+                (activeUri && workspace.getWorkspaceFolder(activeUri)) || workspaceFolders[0];

And update later references to use workspaceFolder instead of workspaceFolders[0].


89-93: Scope “Don’t Ask Again” per workspace

Current key is global; scope it to the chosen workspace to avoid cross‑workspace side effects.

-                const dontAskState = this.persistentStateFactory.createGlobalPersistentState<boolean>(
-                    DONT_ASK_OVERWRITE_REQUIREMENTS_KEY,
-                    false // default: ask user
-                );
+                const scopeKey = `${DONT_ASK_OVERWRITE_REQUIREMENTS_KEY}:${workspaceFolder.uri.toString()}`;
+                const dontAskState =
+                    this.persistentStateFactory.createGlobalPersistentState<boolean>(scopeKey, false);
♻️ Duplicate comments (6)
src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (3)

14-18: Implement the interface.

Class should declare implements IDeepnoteInitNotebookRunner.

 @injectable()
-export class DeepnoteInitNotebookRunner {
+export class DeepnoteInitNotebookRunner implements IDeepnoteInitNotebookRunner {

As per coding guidelines


93-99: Remove brittle cancellation check.

Comparing error.message to 'Cancelled' is fragile. Token checks suffice.

         } catch (error) {
-            // Check if this is a cancellation error
-            if (error instanceof Error && error.message === 'Cancelled') {
-                logger.info(`Init notebook cancelled for project ${projectId}`);
-                return;
-            }
             // Log error but don't throw - we want to let user continue anyway
             logger.error(`Error running init notebook for project ${projectId}:`, error);

As per coding guidelines


137-147: Localize user-facing strings.

Wrap progress titles and messages with l10n.t().

+import { NotebookDocument, ProgressLocation, window, CancellationTokenSource, CancellationToken, l10n } from 'vscode';
@@
-                    title: `🚀 Initializing project environment`,
+                    title: l10n.t('🚀 Initializing project environment'),
@@
-                            title: `Init: "${initNotebook.name}"`,
+                            title: l10n.t('Init: "{0}"', initNotebook.name),

Apply similarly to lines 187, 213-214, 240-242, 270.

As per coding guidelines

src/kernels/execution/cellExecutionMessageHandler.ts (1)

82-88: Helper successfully eliminates duplication.

The extraction of getCellId addresses the previous review's concern about repeated fallback logic. Implementation is clean and consistent across all call sites.

Based on past review comments.

src/webviews/extension-side/dataframe/dataframeController.ts (1)

141-165: Restore cellIndex fallback (goToPage).

Must accept index when id missing or not found; otherwise navigation breaks. This mirrors selectPageSize.

Apply this diff:

-        if (!message.cellId) {
-            const errorMessage =
-                'Unable to navigate to page: No cell identifier provided. ' +
-                'Please re-run the cell to update the output metadata.';
-
-            logger.error(`[DataframeController] ${errorMessage}`);
-
-            await window.showErrorMessage(errorMessage);
-
-            throw new Error(errorMessage);
-        }
-
-        const cells = editor.notebook.getCells();
-        const cell = cells.find((c) => c.metadata.id === message.cellId);
-
-        if (!cell) {
-            const errorMessage = `Unable to navigate to page: Could not find the cell with ID ${message.cellId}. The cell may have been deleted.`;
-
-            logger.error(`[DataframeController] ${errorMessage}`);
-
-            await window.showErrorMessage(errorMessage);
-
-            throw new Error(errorMessage);
-        }
+        const cells = editor.notebook.getCells();
+        let cell = message.cellId ? cells.find((c) => c.metadata.id === message.cellId) : undefined;
+        let cellIndex = cell?.index;
+
+        if (!cell && typeof message.cellIndex === 'number') {
+            const idx = message.cellIndex;
+            if (idx >= 0 && idx < editor.notebook.cellCount) {
+                cell = editor.notebook.cellAt(idx);
+                cellIndex = idx;
+                logger.debug?.(`[DataframeController] goToPage: using cellIndex fallback ${idx}`);
+            }
+        }
+
+        if (!cell || cellIndex === undefined) {
+            const errorMessage =
+                `Unable to navigate to page: Could not resolve target cell by id (${message.cellId ?? 'n/a'}) or index (${message.cellIndex ?? 'n/a'}).`;
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }
src/webviews/webview-side/dataframe-renderer/index.ts (1)

19-21: Unmount React roots in disposeOutputItem.

Without unmount, outputs leak on dispose/re-render.

Apply this diff:

 export const activate: ActivationFunction = (context: RendererContext<unknown>) => {
-    return {
+    const roots = new Map<string, HTMLElement>();
+    return {
         renderOutputItem(outputItem: OutputItem, element: HTMLElement) {
             logMessage(`Dataframe renderer - rendering output item: ${outputItem.id}`);
             try {
                 const data = outputItem.json();
                 logMessage(`Dataframe renderer - received data with ${Object.keys(data).length} keys`);
@@
-                const root = document.createElement('div');
+                const root = document.createElement('div');
                 element.appendChild(root);
+                roots.set(outputItem.id, root);
@@
-                ReactDOM.render(
+                ReactDOM.render(
                     React.createElement(DataframeRenderer, {
                         context,
                         data,
                         metadata: dataFrameMetadata,
                         cellId,
                         cellIndex
                     }),
                     root
                 );
             } catch (error) {
                 logErrorMessage(`Error rendering dataframe: ${error}`);
                 const errorDiv = document.createElement('div');
                 errorDiv.style.padding = '10px';
                 errorDiv.style.color = 'var(--vscode-errorForeground)';
                 errorDiv.textContent = `Error rendering dataframe: ${error}`;
                 element.appendChild(errorDiv);
             }
         },
 
-        disposeOutputItem(_id?: string) {
-            // Cleanup if needed
+        disposeOutputItem(id?: string) {
+            if (id) {
+                const root = roots.get(id);
+                if (root) {
+                    ReactDOM.unmountComponentAtNode(root);
+                    roots.delete(id);
+                }
+            }
         }
     };
 };

Also applies to: 36-48, 59-61

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ba9ab4 and a51a150.

📒 Files selected for processing (8)
  • build/esbuild/build.ts (5 hunks)
  • src/kernels/execution/cellExecutionMessageHandler.ts (4 hunks)
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (1 hunks)
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts (16 hunks)
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (5 hunks)
  • src/webviews/extension-side/dataframe/dataframeController.ts (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • build/esbuild/build.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • build/esbuild/build.ts
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
**/*.node.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.node.ts for Desktop-specific implementations that require full file system access and Python environments

Files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
src/notebooks/deepnote/**

📄 CodeRabbit inference engine (CLAUDE.md)

Deepnote integration code resides under src/notebooks/deepnote/

Files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecutionMessageHandler.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/execution/cellExecutionMessageHandler.ts
🧠 Learnings (7)
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use the `ILogger` service instead of `console.log`

Applied to files:

  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
  • src/webviews/webview-side/dataframe-renderer/index.ts
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use `l10n.t()` for user-facing strings

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteActivationService.ts : deepnoteActivationService.ts manages VS Code activation for Deepnote

Applied to files:

  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
📚 Learning: 2025-09-03T12:54:35.368Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/interactiveWindow.instructions.md:0-0
Timestamp: 2025-09-03T12:54:35.368Z
Learning: Applies to src/interactive-window/{interactiveWindow.ts,interactiveWindowController.ts} : Implement robust kernel error handling: automatic restart, clear error display in system info cells, graceful degradation when kernels are unavailable, and user notifications for critical failures

Applied to files:

  • src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/CellExecutionMessageHandler.ts : CellExecutionMessageHandler must process kernel messages (iopub, execute_reply) and update VS Code cell outputs

Applied to files:

  • src/kernels/execution/cellExecutionMessageHandler.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to **/*.{ts,tsx} : Use Uri.joinPath() to construct file paths instead of manual string concatenation

Applied to files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
📚 Learning: 2025-09-03T13:00:18.307Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/platform.instructions.md:0-0
Timestamp: 2025-09-03T13:00:18.307Z
Learning: Applies to src/platform/**/{fileSystem.ts,fileSystem.node.ts,platformService.web.ts,platformService.node.ts} : Use URI-based file operations and vscode-path for cross-platform path handling in file system and platform service implementations

Applied to files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
🧬 Code graph analysis (4)
src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts (1)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/kernels/execution/cellExecutionMessageHandler.ts (1)
src/kernels/execution/helpers.ts (1)
  • cellOutputToVSCCellOutput (132-170)
src/webviews/webview-side/dataframe-renderer/index.ts (1)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (2)
  • DataframeMetadata (7-9)
  • DataframeRenderer (56-215)
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (1)
src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts (1)
  • injectable (49-556)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build & Test
🔇 Additional comments (13)
src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (1)

237-237: LGTM! Progress calculation fixed.

The off-by-one issue is resolved; final block now correctly reports 100%.

src/notebooks/deepnote/deepnoteKernelAutoSelector.node.ts (1)

15-16: Excellent localization and error handling.

All user-facing strings now use l10n.t() and error logging correctly passes error objects as second parameter. Changes align with coding guidelines.

As per coding guidelines

Also applies to: 131-134, 246-246, 259-259, 283-283, 323-323, 333-333, 345-345, 359-359, 381-381, 415-423, 440-440, 478-478, 515-515, 525-525, 537-537

src/kernels/execution/cellExecutionMessageHandler.ts (3)

645-647: Consistent cell ID propagation in addToCellData.

Correctly applies the helper and passes both cellIndex and cellId to output conversion.


1016-1026: Stream message handling updated consistently.

All three code paths in handleStreamMessage now use the centralized helper. The append, replace, and create-new-output branches all propagate cell identity correctly.

Also applies to: 1032-1042, 1049-1059


1177-1187: Display data updates preserve cell identity.

Update-display-data path correctly retrieves and passes the cell ID, maintaining consistency with the main execution paths.

build/esbuild/build.ts (4)

12-14: LGTM!

Standard imports for PostCSS/Tailwind CSS processing.


92-96: LGTM!

Clean optional parameter for conditional Tailwind processing.


189-191: LGTM!

Clear feature toggle based on source path.


320-324: LGTM! Past review issue resolved.

The dataframe renderer build entry now correctly uses watch: watchAll, consistent with other webview renderers.

src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1)

127-131: Fix non-Tailwind class 'flex-grow-1'.

Use flex-grow (Tailwind) to ensure styles apply.
[suggest_minor_issue]
Apply this diff:

-                            <div key={column.name} className="flex-none flex-grow-1">
+                            <div key={column.name} className="flex-none flex-grow">
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (3)

11-14: DI contract + symbol pattern looks good

Symbol + interface export align with inversify DI and keep types clear.


67-69: Good: Uri.joinPath used for path construction

Correct per guidelines; avoids manual string joins.

As per coding guidelines


96-103: Good: user‑facing strings localized

Using l10n.t for prompt and buttons is correct.

As per coding guidelines

Comment on lines +130 to +139
await workspace.applyEdit(edit);

// Re-execute the cell to apply the new page size
logger.info(`[DataframeRenderer] Re-executing cell ${cellIndex} with new page size`);

await commands.executeCommand('notebook.cell.execute', {
ranges: [{ start: cellIndex, end: cellIndex + 1 }],
document: editor.notebook.uri
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Check applyEdit result and bail if it fails.

applyEdit returns a boolean; handle failures explicitly before re-execution.

Apply this diff:

-        await workspace.applyEdit(edit);
+        const ok = await workspace.applyEdit(edit);
+        if (!ok) {
+            const errorMessage = 'Failed to persist table state to cell metadata.';
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }

Also applies to: 184-193

🤖 Prompt for AI Agents
In src/webviews/extension-side/dataframe/dataframeController.ts around lines
130-139 (and also apply the same change to lines 184-193),
workspace.applyEdit(...) is being awaited but its boolean return value is
ignored; update the code to capture the result, and if it is false log an
error/warning (including context like cellIndex and attempted edit) and return
or otherwise bail out before calling commands.executeCommand to re-execute the
cell. Ensure both occurrences check the boolean, log via the same logger used in
the file, and only proceed to re-execute the notebook cell when applyEdit
returns true.

Comment on lines 201 to 210
private async handleExportDataframe(editor: NotebookEditor, message: ExportDataframeCommand) {
const cell = editor.notebook.cellAt(message.cellIndex);

logger.info(
`[DataframeRenderer] exportDataframe called for cell ${
message.cellIndex
} (${cell?.document.uri.toString()})`
);
// TODO: Implement dataframe export functionality
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard cellIndex before cellAt (prevents throw).

cellAt throws on out-of-bounds. Validate first and show a user-friendly error.

Apply this diff:

-        const cell = editor.notebook.cellAt(message.cellIndex);
-
-        logger.info(
+        const idx = message.cellIndex;
+        if (idx < 0 || idx >= editor.notebook.cellCount) {
+            const errorMessage = `Unable to export dataframe: Invalid cell index ${idx}.`;
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }
+        const cell = editor.notebook.cellAt(idx);
+
+        logger.info(
             `[DataframeRenderer] exportDataframe called for cell ${
-                message.cellIndex
+                idx
             } (${cell?.document.uri.toString()})`
         );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private async handleExportDataframe(editor: NotebookEditor, message: ExportDataframeCommand) {
const cell = editor.notebook.cellAt(message.cellIndex);
logger.info(
`[DataframeRenderer] exportDataframe called for cell ${
message.cellIndex
} (${cell?.document.uri.toString()})`
);
// TODO: Implement dataframe export functionality
}
private async handleExportDataframe(editor: NotebookEditor, message: ExportDataframeCommand) {
const idx = message.cellIndex;
if (idx < 0 || idx >= editor.notebook.cellCount) {
const errorMessage = `Unable to export dataframe: Invalid cell index ${idx}.`;
logger.error(`[DataframeController] ${errorMessage}`);
await window.showErrorMessage(errorMessage);
throw new Error(errorMessage);
}
const cell = editor.notebook.cellAt(idx);
logger.info(
`[DataframeRenderer] exportDataframe called for cell ${
idx
} (${cell?.document.uri.toString()})`
);
// TODO: Implement dataframe export functionality
}
🤖 Prompt for AI Agents
In src/webviews/extension-side/dataframe/dataframeController.ts around lines 201
to 210, the code calls editor.notebook.cellAt(message.cellIndex) without
validating message.cellIndex; cellAt throws for out-of-bounds indices. Fix by
first checking that message.cellIndex is a non-negative integer less than
editor.notebook.cellCount (and that editor and editor.notebook exist), and if
invalid call a user-friendly error path (e.g., logger.warn and
vscode.window.showErrorMessage or similar) and return early; otherwise proceed
to call cellAt and continue with the export logic.

Comment on lines 63 to 67
logMessage(
`[DataframeRenderer] Rendering with cellId: ${cellId}, cellIndex: ${cellIndex}, data: ${JSON.stringify(
data
)}, metadata: ${JSON.stringify(metadata)}`
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Reduce log volume (optional).

Logging full data/metadata is noisy. Prefer concise summaries in prod.

Replace JSON.stringify payloads with brief counts/ids, gated behind a debug flag if available.

Also applies to: 73-75

🤖 Prompt for AI Agents
In src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx around
lines 63-67 (and similarly lines 73-75), the current log prints full data and
metadata via JSON.stringify which floods logs; change the log to emit concise
summaries instead (e.g., data length/row count, key IDs, or first N items) and
only include full JSON when a debug flag is enabled; implement gating by
checking the existing debug/config flag (or process.env.NODE_ENV !==
'production' / a local DEBUG flag) so production logs remain small while full
payloads are available when debugging.

Comment on lines 22 to 35
logMessage(`Dataframe renderer - rendering output item: ${outputItem.id}`);
try {
const data = outputItem.json();
logMessage(`Dataframe renderer - received data with ${Object.keys(data).length} keys`);

const metadata = outputItem.metadata as Metadata | undefined;
logMessage(`[DataframeRenderer] Full metadata: ${JSON.stringify(metadata)}`);

const dataFrameMetadata = metadata?.metadata as DataframeMetadata | undefined;
const cellId = metadata?.cellId;
const cellIndex = metadata?.cellIndex;

logMessage(`[DataframeRenderer] Extracted cellId: ${cellId}, cellIndex: ${cellIndex}`);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Trim verbose logs (avoid dumping data/metadata).

Large JSON logs hurt perf and risk PII. Log summaries only.

Apply this diff:

-            logMessage(`Dataframe renderer - rendering output item: ${outputItem.id}`);
+            logMessage(`Dataframe renderer: render ${outputItem.id}`);
@@
-                logMessage(`Dataframe renderer - received data with ${Object.keys(data).length} keys`);
+                logMessage(`Dataframe renderer: data keys=${Object.keys(data).length}`);
@@
-                logMessage(`[DataframeRenderer] Full metadata: ${JSON.stringify(metadata)}`);
+                logMessage(`[DataframeRenderer] metadata present=${!!metadata}`);
@@
-                logMessage(`[DataframeRenderer] Extracted cellId: ${cellId}, cellIndex: ${cellIndex}`);
+                logMessage(`[DataframeRenderer] cellId=${cellId ?? 'n/a'} cellIndex=${cellIndex ?? 'n/a'}`);
@@
-                logErrorMessage(`Error rendering dataframe: ${error}`);
+                logErrorMessage(`Error rendering dataframe: ${String(error)}`);

Also applies to: 49-56

🤖 Prompt for AI Agents
In src/webviews/webview-side/dataframe-renderer/index.ts around lines 22-35 (and
similarly lines 49-56), current logs dump full data/metadata JSON which is
verbose and may contain PII; replace those statements with concise summaries:
log counts, types, presence flags and identifiers only (e.g., number of keys,
whether metadata exists, data shape info, cellId/cellIndex) and remove
JSON.stringify outputs; ensure logging calls avoid printing full object contents
and instead use small deterministic strings.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
build/esbuild/build.ts (1)

166-180: Structural error: premature callback closure.

Line 167 closes the onLoad handler prematurely. Lines 169-180 reference args but are outside the callback scope, causing the pipeline syntax error at line 180.

Remove line 167. The default CSS processing (lines 169-180) must be inside the onLoad callback:

-                }
-                // …rest of the onLoad handler…
-            });
-
                 // Default behavior for other CSS files
                 const options = { entryPoints: [args.path], ...opt };
                 options.loader = options.loader || {};
                 // Add the same loaders we add for other places
                 Object.keys(loader).forEach((key) => {
                     if (options.loader && !options.loader[key]) {
                         options.loader[key] = loader[key];
                     }
                 });
                 const { errors, warnings, outputFiles } = await esbuild.build(options);
                 return { errors, warnings, contents: outputFiles![0].text, loader: 'text' };
             });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a51a150 and 98d7fe6.

📒 Files selected for processing (1)
  • build/esbuild/build.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • build/esbuild/build.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • build/esbuild/build.ts
🧬 Code graph analysis (1)
build/esbuild/build.ts (1)
build/webpack/common.js (1)
  • path (6-6)
🪛 GitHub Actions: CI
build/esbuild/build.ts

[error] 180-180: SyntaxError: ',' expected. (180:14) during Prettier check in 'npm run format'.

🔇 Additional comments (4)
build/esbuild/build.ts (4)

92-96: LGTM!

The enableTailwind parameter enables conditional Tailwind processing.


196-198: LGTM!

Tailwind processing is correctly enabled only for the dataframe renderer.


327-331: LGTM!

The dataframe renderer build correctly uses watchAll for consistency with other webview renderers.


12-14: Dependencies are up-to-date. Installed versions of postcss (8.5.6), @tailwindcss/postcss (4.1.14), tailwindcss (4.1.14), and autoprefixer (10.4.21) match the latest stable releases.


onLoad({ filter: /.*/, namespace: 'style-content' }, async (args) => {
// Process with PostCSS/Tailwind if enabled and file exists
if (enableTailwind && args.path.includes('tailwind.css') && fs.existsSync(args.path)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded filename check is fragile.

The check args.path.includes('tailwind.css') assumes a specific filename. Renaming or adding multiple Tailwind files will break processing.

Consider a more flexible approach (e.g., check for @tailwind directives in content or use a pattern/config).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (7)
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (1)

160-162: Remove absolute path from log (PII).

Line 161 logs requirementsPath (absolute filesystem path). Use workspace name instead.

Apply this diff:

-            this.logger.info(
-                `Created requirements.txt with ${normalizedRequirements.length} dependencies at ${requirementsPath}`
-            );
+            this.logger.info(
+                `Created requirements.txt with ${normalizedRequirements.length} dependencies in workspace "${workspaceFolders[0].name}"`
+            );

As per coding guidelines

build/esbuild/build.ts (1)

143-143: Hardcoded filename check remains fragile.

The check args.path.includes('tailwind.css') was flagged in previous reviews but not addressed. Renaming or adding multiple Tailwind files will break processing.

Consider checking for @tailwind directives in content or use a configurable pattern.

src/webviews/extension-side/dataframe/dataframeController.ts (5)

63-86: Stop logging payloads.

Still logging the entire message, leaking table contents. Log only whitelisted fields and skip noisy copy commands. As per coding guidelines.

-        logger.info('DataframeController received message', message);
+        const { command } = message;
+        if (command !== 'copyTableData') {
+            logger.info('DataframeController received message', {
+                command,
+                cellId: (message as { cellId?: string }).cellId,
+                cellIndex: (message as { cellIndex?: number }).cellIndex
+            });
+        }

101-138: Restore cellIndex fallback for selectPageSize.

Renderer still sends index-only messages; ignoring them breaks paging. Reintroduce index validation and fallback, and surface both identifiers in the error.

-        const cells = editor.notebook.getCells();
-        const cell = cells.find((c) => c.metadata.id === message.cellId);
-
-        if (!cell) {
-            const errorMessage = l10n.t(
-                'Unable to update page size: Could not find the cell with ID {0}. The cell may have been deleted.',
-                message.cellId ?? ''
-            );
+        const cells = editor.notebook.getCells();
+        let cell = message.cellId ? cells.find((c) => c.metadata.id === message.cellId) : undefined;
+        let cellIndex = cell?.index;
+
+        if (!cell && typeof message.cellIndex === 'number' && Number.isInteger(message.cellIndex)) {
+            const idx = message.cellIndex;
+            if (idx >= 0 && idx < editor.notebook.cellCount) {
+                cell = editor.notebook.cellAt(idx);
+                cellIndex = idx;
+                logger.info(`[DataframeController] selectPageSize: resolved via cellIndex ${idx}`);
+            }
+        }
+
+        if (!cell || cellIndex === undefined) {
+            const errorMessage = l10n.t(
+                'Unable to update page size: Could not resolve target cell by id ({0}) or index ({1}).',
+                message.cellId ?? 'n/a',
+                message.cellIndex ?? 'n/a'
+            );
 
             logger.error(`[DataframeController] ${errorMessage}`);
 
             await window.showErrorMessage(errorMessage);
 
             throw new Error(errorMessage);
         }
-
-        const cellIndex = cell.index;

134-199: Handle applyEdit failures before re-running cells.

workspace.applyEdit can fail; re-executing after a rejected edit leaves the UI wrong. Capture the boolean and bail. Apply the same guard in the later block.

-        await workspace.applyEdit(edit);
+        const editApplied = await workspace.applyEdit(edit);
+        if (!editApplied) {
+            const errorMessage = l10n.t('Failed to persist table state to cell metadata.');
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }

145-200: Go-to-page still lacks cellIndex fallback.

Renderer keeps sending index-only messages; forcing cellId breaks navigation. Accept a valid index when id lookup fails and align the error text.

-        if (!message.cellId) {
-            const errorMessage = l10n.t(
-                'Unable to navigate to page: No cell identifier provided. Please re-run the cell to update the output metadata.'
-            );
+        if (message.cellId === undefined && message.cellIndex === undefined) {
+            const errorMessage = l10n.t(
+                'Unable to navigate to page: No cell identifier provided. Please re-run the cell to update the output metadata.'
+            );
 
             logger.error(`[DataframeController] ${errorMessage}`);
 
             await window.showErrorMessage(errorMessage);
 
             throw new Error(errorMessage);
         }
 
         const cells = editor.notebook.getCells();
-        const cell = cells.find((c) => c.metadata.id === message.cellId);
-
-        if (!cell) {
-            const errorMessage = l10n.t(
-                'Unable to navigate to page: Could not find the cell with ID {0}. The cell may have been deleted.',
-                message.cellId ?? ''
-            );
+        let cell = message.cellId ? cells.find((c) => c.metadata.id === message.cellId) : undefined;
+        let cellIndex = cell?.index;
+
+        if (!cell && typeof message.cellIndex === 'number' && Number.isInteger(message.cellIndex)) {
+            const idx = message.cellIndex;
+            if (idx >= 0 && idx < editor.notebook.cellCount) {
+                cell = editor.notebook.cellAt(idx);
+                cellIndex = idx;
+                logger.info(`[DataframeController] goToPage: resolved via cellIndex ${idx}`);
+            }
+        }
+
+        if (!cell || cellIndex === undefined) {
+            const errorMessage = l10n.t(
+                'Unable to navigate to page: Could not resolve target cell by id ({0}) or index ({1}).',
+                message.cellId ?? 'n/a',
+                message.cellIndex ?? 'n/a'
+            );
 
             logger.error(`[DataframeController] ${errorMessage}`);
 
             await window.showErrorMessage(errorMessage);
 
             throw new Error(errorMessage);
         }
 
         // Update page index in table state within cell metadata
         const existingTableState = cell.metadata.deepnote_table_state || {};
         const updatedTableState = {
             ...existingTableState,
             pageIndex: message.page
         };
 
-        const cellIndex = cell.index;
+        const cellIndex = cellIndex!;

208-215: Validate export cellIndex before cellAt.

cellAt throws on out-of-range indices. Guard and raise a localized error instead of crashing.

-        const cell = editor.notebook.cellAt(message.cellIndex);
+        const { cellIndex } = message;
+        if (cellIndex < 0 || cellIndex >= editor.notebook.cellCount) {
+            const errorMessage = l10n.t('Unable to export dataframe: Invalid cell index {0}.', cellIndex);
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }
+
+        const cell = editor.notebook.cellAt(cellIndex);
 
         logger.info(
             `[DataframeRenderer] exportDataframe called for cell ${
-                message.cellIndex
+                cellIndex
             } (${cell?.document.uri.toString()})`
         );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98d7fe6 and 3a4d579.

📒 Files selected for processing (3)
  • build/esbuild/build.ts (6 hunks)
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (6 hunks)
  • src/webviews/extension-side/dataframe/dataframeController.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • build/esbuild/build.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • build/esbuild/build.ts
  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
**/*.node.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.node.ts for Desktop-specific implementations that require full file system access and Python environments

Files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
src/notebooks/deepnote/**

📄 CodeRabbit inference engine (CLAUDE.md)

Deepnote integration code resides under src/notebooks/deepnote/

Files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
🧠 Learnings (5)
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to **/*.{ts,tsx} : Use Uri.joinPath() to construct file paths instead of manual string concatenation

Applied to files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
📚 Learning: 2025-09-03T13:00:18.307Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/platform.instructions.md:0-0
Timestamp: 2025-09-03T13:00:18.307Z
Learning: Applies to src/platform/**/{fileSystem.ts,fileSystem.node.ts,platformService.web.ts,platformService.node.ts} : Use URI-based file operations and vscode-path for cross-platform path handling in file system and platform service implementations

Applied to files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use `l10n.t()` for user-facing strings

Applied to files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
📚 Learning: 2025-09-24T21:29:29.065Z
Learnt from: jankuca
PR: deepnote/deepnote#18525
File: apps/webapp-client/src/features/project-table-of-contents/markdown-toc-utils.test.ts:131-169
Timestamp: 2025-09-24T21:29:29.065Z
Learning: In the Deepnote codebase, CRLF line endings are automatically converted to LF on save, so testing CRLF scenarios in markdown parsing functions is unnecessary.

Applied to files:

  • src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteActivationService.ts : deepnoteActivationService.ts manages VS Code activation for Deepnote

Applied to files:

  • src/webviews/extension-side/dataframe/dataframeController.ts
🧬 Code graph analysis (1)
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (1)
src/test/mocks/vsc/index.ts (1)
  • CancellationToken (100-112)
🔇 Additional comments (7)
src/notebooks/deepnote/deepnoteRequirementsHelper.node.ts (4)

19-24: LGTM!

Interface implementation and DI setup follow best practices.


44-57: LGTM!

Normalization logic correctly filters non-strings, trims, removes empties, and deduplicates using Set. Type guard ensures string[] downstream.


77-92: LGTM!

EOL normalization avoids CRLF/LF mismatches that would trigger needless file rewrites.


34-36: LGTM!

Cancellation checks at key points (start, pre-I/O, post-prompt, post-write) ensure responsive cancellation handling.

Also applies to: 67-69, 120-122, 155-158

build/esbuild/build.ts (3)

12-14: LGTM!

PostCSS toolchain imports are correct and properly used.


92-96: LGTM!

Backward-compatible addition of enableTailwind parameter.


327-331: LGTM!

Build entry correctly uses watchAll for watch flag consistency with other webview renderers.

Comment on lines +196 to +198
// Enable Tailwind processing for dataframe renderer
const enableTailwind = source.includes(path.join('dataframe-renderer', 'index.ts'));
plugins.push(style({ enableTailwind }));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider more robust path matching.

The check source.includes(path.join('dataframe-renderer', 'index.ts')) is fragile if the file is moved or renamed. Consider a more explicit path comparison or document the coupling.

Example:

-        const enableTailwind = source.includes(path.join('dataframe-renderer', 'index.ts'));
+        const enableTailwind = source === path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'dataframe-renderer', 'index.ts');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Enable Tailwind processing for dataframe renderer
const enableTailwind = source.includes(path.join('dataframe-renderer', 'index.ts'));
plugins.push(style({ enableTailwind }));
// Enable Tailwind processing for dataframe renderer
const enableTailwind = source === path.join(
extensionFolder,
'src',
'webviews',
'webview-side',
'dataframe-renderer',
'index.ts'
);
plugins.push(style({ enableTailwind }));
🤖 Prompt for AI Agents
In build/esbuild/build.ts around lines 196 to 198, the current
source.includes(path.join('dataframe-renderer', 'index.ts')) check is brittle;
replace it with a more robust path comparison such as normalizing the path and
testing for a suffix (e.g.,
path.normalize(source).endsWith(path.join('dataframe-renderer','index.ts'))) or
resolving and comparing relative paths from the project root, or match by
basename + parent directory (e.g., path.basename(source) === 'index.ts' &&
path.dirname(path.normalize(source)).endsWith('dataframe-renderer'));
alternatively, if coupling is intentional, add a comment documenting that this
exact path is required and why.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/kernels/execution/helpers.ts (1)

178-223: Avoid metadata key collisions when merging block metadata

Block metadata is merged over cellId/cellIndex and can overwrite them. If that’s not intended, namespace block metadata (e.g., metadata.block = cellMetadata) or set cellId/cellIndex last.

src/notebooks/deepnote/deepnoteDataConverter.ts (1)

205-226: Fix object spread of possibly undefined blockMetadata

Using ...blockMetadata throws when undefined. Use nullish coalescing.

Apply this diff:

-                    const metadata: Record<string, unknown> = {
-                        cellId,
-                        ...blockMetadata,
-                        cellIndex
-                    };
+                    const metadata: Record<string, unknown> = {
+                        cellId,
+                        ...(blockMetadata ?? {}),
+                        cellIndex
+                    };
@@
-                    const metadata: Record<string, unknown> = {
-                        cellId,
-                        ...blockMetadata,
-                        cellIndex
-                    };
+                    const metadata: Record<string, unknown> = {
+                        cellId,
+                        ...(blockMetadata ?? {}),
+                        cellIndex
+                    };
@@
-                    const metadata: Record<string, unknown> = {
-                        cellId,
-                        ...blockMetadata,
-                        cellIndex
-                    };
+                    const metadata: Record<string, unknown> = {
+                        cellId,
+                        ...(blockMetadata ?? {}),
+                        cellIndex
+                    };
@@
-                    const metadata: Record<string, unknown> = {
-                        cellId,
-                        ...blockMetadata,
-                        cellIndex
-                    };
+                    const metadata: Record<string, unknown> = {
+                        cellId,
+                        ...(blockMetadata ?? {}),
+                        cellIndex
+                    };
@@
-                const metadata: Record<string, unknown> = {
-                    cellId,
-                    ...blockMetadata,
-                    cellIndex
-                };
+                const metadata: Record<string, unknown> = {
+                    cellId,
+                    ...(blockMetadata ?? {}),
+                    cellIndex
+                };

Also applies to: 289-304, 316-326, 329-340, 347-358

♻️ Duplicate comments (5)
build/esbuild/build.ts (3)

141-165: Fragile filename check and logging violation.

Two issues:

  1. Line 143: Hardcoded 'tailwind.css' check is fragile. Renaming the file or adding multiple Tailwind files will break processing.

  2. Line 162: Uses console.error instead of the ILogger service, violating coding guidelines.

For issue 1, consider checking for @tailwind directives in content or using a configurable pattern. For issue 2, inject and use ILogger:

-                        console.error(`PostCSS processing failed for ${args.path}:`, error);
+                        // Use ILogger service instead of console.error

196-198: Fragile path check.

Line 197 uses source.includes(path.join('dataframe-renderer', 'index.ts')), which is brittle if the file is moved or renamed.

Consider a more robust approach:

-        const enableTailwind = source.includes(path.join('dataframe-renderer', 'index.ts'));
+        const enableTailwind = path.normalize(source).endsWith(path.join('dataframe-renderer', 'index.ts'));

Or explicitly match the full path:

-        const enableTailwind = source.includes(path.join('dataframe-renderer', 'index.ts'));
+        const enableTailwind = source === path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'dataframe-renderer', 'index.ts');

327-331: Inconsistent watch flag.

Line 330 uses watch: isWatchMode while other webview renderers (lines 313, 325, 335, 340, 345) use watch: watchAll. This renderer won't rebuild when running npm run watch-all.

Apply this diff:

         build(
             path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'dataframe-renderer', 'index.ts'),
             path.join(extensionFolder, 'dist', 'webviews', 'webview-side', 'dataframeRenderer', 'dataframeRenderer.js'),
-            { target: 'web', watch: isWatchMode }
+            { target: 'web', watch: watchAll }
         ),
src/notebooks/deepnote/deepnoteDataConverter.ts (1)

262-270: atob/btoa will crash under Node; make base64 conversion cross‑runtime

base64ToUint8Array uses atob (browser‑only). This file runs in the extension host (Node). Replace helper to support both Web and Node; also update reverse (btoa) usage.

Apply this diff for the helper:

-    private base64ToUint8Array(base64: string): Uint8Array {
-        const binaryString = atob(base64);
-        const bytes = new Uint8Array(binaryString.length);
-        for (let i = 0; i < binaryString.length; i++) {
-            bytes[i] = binaryString.charCodeAt(i);
-        }
-        return bytes;
-    }
+    private base64ToUint8Array(base64: string): Uint8Array {
+        // Browser path
+        if (typeof (globalThis as any).atob === 'function') {
+            const binaryString = (globalThis as any).atob(base64);
+            const bytes = new Uint8Array(binaryString.length);
+            for (let i = 0; i < binaryString.length; i++) {
+                bytes[i] = binaryString.charCodeAt(i);
+            }
+            return bytes;
+        }
+        // Node path
+        const Buf: any = (globalThis as any).Buffer;
+        if (Buf && typeof Buf.from === 'function') {
+            return new Uint8Array(Buf.from(base64, 'base64'));
+        }
+        return new Uint8Array();
+    }

And replace reverse encoding in transformOutputsForDeepnote (outside this hunk) to avoid btoa:

-                } else if (item.mime === 'image/png') {
-                    data['image/png'] = btoa(String.fromCharCode(...new Uint8Array(item.data)));
-                } else if (item.mime === 'image/jpeg') {
-                    data['image/jpeg'] = btoa(String.fromCharCode(...new Uint8Array(item.data)));
+                } else if (item.mime === 'image/png') {
+                    // Node: Buffer to base64, Browser: btoa fallback
+                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+                    const Buf: any = (globalThis as any).Buffer;
+                    data['image/png'] = Buf
+                        ? Buf.from(item.data).toString('base64')
+                        : btoa(String.fromCharCode(...new Uint8Array(item.data)));
+                } else if (item.mime === 'image/jpeg') {
+                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+                    const Buf: any = (globalThis as any).Buffer;
+                    data['image/jpeg'] = Buf
+                        ? Buf.from(item.data).toString('base64')
+                        : btoa(String.fromCharCode(...new Uint8Array(item.data)));

Also applies to: 273-279

src/webviews/webview-side/dataframe-renderer/index.ts (1)

21-21: Console usage remains - see past review comments.

Console.log/error calls are still present. Past comments flagged this and indicated it was addressed in commit a51a150, but the current code still contains these calls.

Per coding guidelines: use ILogger service for all logging in .ts/.tsx files.

Also applies to: 24-24, 27-27, 33-33, 49-49

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a4d579 and 4fa78e2.

📒 Files selected for processing (9)
  • build/esbuild/build.ts (6 hunks)
  • src/kernels/execution/cellExecutionMessageHandler.ts (4 hunks)
  • src/kernels/execution/helpers.ts (10 hunks)
  • src/notebooks/deepnote/blocks.md (1 hunks)
  • src/notebooks/deepnote/deepnoteDataConverter.ts (9 hunks)
  • src/notebooks/deepnote/pocket.ts (3 hunks)
  • src/notebooks/deepnote/serialization.md (0 hunks)
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (1 hunks)
  • src/webviews/webview-side/dataframe-renderer/index.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/notebooks/deepnote/serialization.md
🧰 Additional context used
📓 Path-based instructions (6)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/notebooks/deepnote/pocket.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/kernels/execution/helpers.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • build/esbuild/build.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • src/notebooks/deepnote/pocket.ts
  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/kernels/execution/helpers.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • build/esbuild/build.ts
src/notebooks/deepnote/**

📄 CodeRabbit inference engine (CLAUDE.md)

Deepnote integration code resides under src/notebooks/deepnote/

Files:

  • src/notebooks/deepnote/pocket.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/notebooks/deepnote/blocks.md
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/helpers.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/execution/helpers.ts
  • src/kernels/execution/cellExecutionMessageHandler.ts
src/notebooks/deepnote/deepnoteDataConverter.ts

📄 CodeRabbit inference engine (CLAUDE.md)

deepnoteDataConverter.ts performs Deepnote data transformations

Files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
🧠 Learnings (5)
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteTypes.ts : deepnoteTypes.ts contains Deepnote-related type definitions

Applied to files:

  • src/notebooks/deepnote/pocket.ts
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use the `ILogger` service instead of `console.log`

Applied to files:

  • src/webviews/webview-side/dataframe-renderer/index.ts
  • src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/CellExecutionMessageHandler.ts : CellExecutionMessageHandler must process kernel messages (iopub, execute_reply) and update VS Code cell outputs

Applied to files:

  • src/kernels/execution/cellExecutionMessageHandler.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteDataConverter.ts : deepnoteDataConverter.ts performs Deepnote data transformations

Applied to files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/export/**/*.ts : Update FileConverter to handle the new export format

Applied to files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
🧬 Code graph analysis (5)
src/notebooks/deepnote/pocket.ts (1)
src/notebooks/deepnote/dataConversionUtils.ts (1)
  • generateBlockId (8-15)
src/webviews/webview-side/dataframe-renderer/index.ts (2)
src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx (2)
  • DataframeMetadata (6-8)
  • DataframeRenderer (54-210)
src/webviews/extension-side/dataframe/dataframeController.ts (1)
  • activate (54-57)
src/kernels/execution/helpers.ts (1)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • NotebookCellOutput (71-103)
  • NotebookCellOutputItem (16-69)
src/kernels/execution/cellExecutionMessageHandler.ts (1)
src/kernels/execution/helpers.ts (1)
  • cellOutputToVSCCellOutput (137-176)
src/notebooks/deepnote/deepnoteDataConverter.ts (4)
src/notebooks/deepnote/pocket.ts (1)
  • addPocketToCellMetadata (20-42)
src/notebooks/deepnote/deepnoteSerializer.ts (1)
  • DeepnoteOutput (9-9)
src/notebooks/deepnote/deepnoteTypes.ts (1)
  • DeepnoteOutput (23-51)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • NotebookCellOutput (71-103)
  • NotebookCellOutputItem (16-69)
🪛 markdownlint-cli2 (0.18.1)
src/notebooks/deepnote/blocks.md

15-15: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build & Test
🔇 Additional comments (10)
build/esbuild/build.ts (2)

12-14: LGTM!

Imports are appropriate for PostCSS/Tailwind integration.


92-96: LGTM!

Function signature properly extended with optional enableTailwind parameter.

src/notebooks/deepnote/pocket.ts (1)

52-60: Top‑level id flow is sound

Reading id from top‑level metadata, removing it from metadata, and assigning block.id is correct and keeps VS Code outputs separate from pocket. LGTM.

Also applies to: 66-73

src/kernels/execution/helpers.ts (3)

118-126: API expansion for per‑cell provenance: good

Mapper/caller signatures now carry cellIndex, cellId, cellMetadata and are wired correctly.

Also applies to: 136-175


238-274: Consistent provenance on all output types

display_data/execute_result, stream, and error now include cellId/cellIndex in metadata. Looks good.

Please confirm downstream consumers read these fields from output.metadata (not output.items[].metadata).

Also applies to: 276-285, 596-613


329-336: CellOutputMetadata additions look correct

Adding cellIndex and cellId to the metadata interface aligns with usage.

src/kernels/execution/cellExecutionMessageHandler.ts (2)

82-88: Centralized cellId helper

Good extraction. Prefers Deepnote metadata id with URI fallback.


645-649: Per‑cell context propagation: good

Passing cell.index, cellId, and cell.metadata into output translation across paths is consistent. LGTM.

Also applies to: 1017-1029, 1036-1047, 1055-1066, 1185-1196

src/notebooks/deepnote/deepnoteDataConverter.ts (1)

31-31: Block → cell metadata and outputs: good

  • Passing block.id to metadata.id and to output transformation aligns with new pocket semantics.
  • Converting VS Code outputs back to Deepnote only when present is fine.

Please confirm tests cover cells without block.metadata (to exercise the (blockMetadata ?? {}) path).

Also applies to: 56-57, 80-84

src/notebooks/deepnote/blocks.md (1)

1-628: Excellent comprehensive documentation.

This document thoroughly covers the Deepnote blocks architecture - three representations, pocket system, converters, metadata flow, and includes detailed examples. Well-structured and accurate based on the codebase context.

Comment on lines +15 to +32
```
┌─────────────────┐
│ .deepnote File │
│ (YAML format) │
└────────┬────────┘
│ Deserialize (serializer)
┌─────────────────┐
│ VS Code Cell │
│ (with pocket) │
└────────┬────────┘
│ Execute (cellExecution)
┌─────────────────┐
│ Python Code │
│ (via blocks) │
└─────────────────┘
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language specifier to fenced code block.

The ASCII diagram should specify a language (or use text if no syntax highlighting needed).

Apply this diff:

-```
+```text
 ┌─────────────────┐
 │  .deepnote File │

As per static analysis hint.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

15-15: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In src/notebooks/deepnote/blocks.md around lines 15 to 32, the fenced code block
containing the ASCII diagram lacks a language specifier; update the opening
fence to include a language (use "text") so the block starts with ```text and
add the provided first two lines from the diff inside the block (i.e., the
leading " ┌─────────────────┐" and " │  .deepnote File │") to match the
requested change.

Comment on lines +60 to +65
console.log('[DataframeRenderer] Rendering dataframe', {
cellId,
data,
metadata
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove console logs or use logger

Replace console.log with the extension’s logging facility or gate behind a debug flag; avoid noisy logs in production.

As per coding guidelines.

Also applies to: 70-73, 85-96, 101-112

🤖 Prompt for AI Agents
In src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx around
lines 60-65 (and also apply same changes to lines 70-73, 85-96, 101-112): remove
all console.log/console.debug calls and replace them with the extension's
logging utility (or wrap them behind a debug flag), e.g. call the centralized
logger with appropriate levels and contextual fields or gate the logs by a
boolean isDebug before logging; ensure imports and typing for the logger are
added if missing and preserve the original log message content and structured
data when calling the logger.

Comment on lines +66 to +69
const tableState = useMemo((): TableState => JSON.parse(metadata?.table_state_spec || '{}'), [metadata]);
const [pageSize, setPageSize] = useState(tableState.pageSize || 10);
const [pageIndex, setPageIndex] = useState(tableState.pageIndex || 0);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden table_state_spec parsing

JSON.parse can throw. Add try/catch and safe defaults.

Apply this diff:

-    const tableState = useMemo((): TableState => JSON.parse(metadata?.table_state_spec || '{}'), [metadata]);
-    const [pageSize, setPageSize] = useState(tableState.pageSize || 10);
-    const [pageIndex, setPageIndex] = useState(tableState.pageIndex || 0);
+    const tableState = useMemo((): TableState => {
+        const spec = metadata?.table_state_spec;
+        if (!spec) {
+            return {} as unknown as TableState;
+        }
+        try {
+            return JSON.parse(spec) as TableState;
+        } catch {
+            return {} as unknown as TableState;
+        }
+    }, [metadata]);
+    const [pageSize, setPageSize] = useState<number>(Number(tableState.pageSize) > 0 ? Number(tableState.pageSize) : 10);
+    const [pageIndex, setPageIndex] = useState<number>(Number.isInteger(tableState.pageIndex) && tableState.pageIndex >= 0 ? tableState.pageIndex : 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const tableState = useMemo((): TableState => JSON.parse(metadata?.table_state_spec || '{}'), [metadata]);
const [pageSize, setPageSize] = useState(tableState.pageSize || 10);
const [pageIndex, setPageIndex] = useState(tableState.pageIndex || 0);
const tableState = useMemo((): TableState => {
const spec = metadata?.table_state_spec;
if (!spec) {
return {} as unknown as TableState;
}
try {
return JSON.parse(spec) as TableState;
} catch {
return {} as unknown as TableState;
}
}, [metadata]);
const [pageSize, setPageSize] = useState<number>(
Number(tableState.pageSize) > 0 ? Number(tableState.pageSize) : 10
);
const [pageIndex, setPageIndex] = useState<number>(
Number.isInteger(tableState.pageIndex) && tableState.pageIndex >= 0
? tableState.pageIndex
: 0
);
🤖 Prompt for AI Agents
In src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx around
lines 66 to 69, the JSON.parse call on metadata?.table_state_spec can throw and
currently isn't protected; wrap the parse in a try/catch inside the useMemo so
parsing errors return a safe default object (e.g., {}), and then derive pageSize
and pageIndex from that safe tableState; ensure useMemo depends on metadata and
that pageSize/pageIndex state initializers use the validated values (with
fallbacks like 10 and 0).

Comment on lines +98 to +112
const handlePageChange = (newPageIndex: number) => {
setPageIndex(newPageIndex);

console.log(`[DataframeRenderer] handlePageChange called with cellId: ${cellId}, page: ${newPageIndex}`);

const message = {
command: 'goToPage',
cellId,
page: newPageIndex
};

console.log(`[DataframeRenderer] Posting message: ${JSON.stringify(message)}`);

context.postMessage?.(message);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clamp pagination inputs

Ensure page changes stay within [0, totalPages‑1] even if called programmatically.

Apply this diff:

-    const handlePageChange = (newPageIndex: number) => {
-        setPageIndex(newPageIndex);
+    const handlePageChange = (newPageIndex: number) => {
+        const clamped = Math.max(0, Math.min(newPageIndex, totalPages - 1));
+        setPageIndex(clamped);
@@
-            page: newPageIndex
+            page: clamped

Also applies to: 170-204

🤖 Prompt for AI Agents
In src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx around
lines 98-112 (and similarly for the handler at 170-204), the handlePageChange
currently accepts any newPageIndex; clamp the value to the valid range [0,
totalPages - 1] before calling setPageIndex and posting the message. Implement a
clampedPage = Math.max(0, Math.min(newPageIndex, Math.max(0, totalPages - 1)))
(or equivalent), use clampedPage for setPageIndex and in the posted message, and
handle the edge case where totalPages is 0 by setting page to 0; ensure both
locations use the same clamping logic.

Comment on lines +137 to +139
{value === null || value === undefined ? 'None' : String(value)}
</div>
))}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Localize user‑facing strings

Wrap “None”, “rows, columns”, “Previous page”, “Next page”, “Page X of Y”, and “/ page” in l10n.t().

As per coding guidelines.

Also applies to: 146-151, 170-204

🤖 Prompt for AI Agents
In src/webviews/webview-side/dataframe-renderer/DataframeRenderer.tsx around
lines 137-139 (and similarly for 146-151 and 170-204), user-facing literal
strings ("None", "rows, columns", "Previous page", "Next page", "Page X of Y",
"/ page") must be wrapped with l10n.t(); replace the raw literals with calls to
l10n.t('...') (using appropriate keys or the literal string as the message) and
ensure any interpolated values use l10n.t with placeholders where needed (e.g.,
l10n.t('Page {current} of {total}', { current, total })). Make sure to import
l10n if not already present and run a quick search in the file for other
user-facing literals to wrap similarly.

Comment on lines +38 to +46
ReactDOM.render(
React.createElement(DataframeRenderer, {
context,
data,
metadata: dataFrameMetadata,
cellId,
cellIndex
}),
root
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ReactDOM.unmount still missing - memory leak.

ReactDOM.render mounts components without cleanup in disposeOutputItem, causing memory leaks when outputs are disposed. Past review comment flagged this but it remains unaddressed.

Store root elements and call ReactDOM.unmountComponentAtNode(root) in disposeOutputItem:

 export const activate: ActivationFunction = (context: RendererContext<unknown>) => {
+    const roots = new Map<string, HTMLElement>();
+
     return {
         renderOutputItem(outputItem: OutputItem, element: HTMLElement) {
             console.log(`Dataframe renderer - rendering output item: ${outputItem.id}`);
             try {
                 const data = outputItem.json();
                 console.log(`Dataframe renderer - received data with ${Object.keys(data).length} keys`);
 
                 const metadata = outputItem.metadata as Metadata | undefined;
                 console.log('[DataframeRenderer] Full metadata', metadata);
 
                 const dataFrameMetadata = metadata?.metadata as DataframeMetadata | undefined;
                 const cellId = metadata?.cellId;
                 const cellIndex = metadata?.cellIndex;
 
                 console.log(`[DataframeRenderer] Extracted cellId: ${cellId}, cellIndex: ${cellIndex}`);
 
                 const root = document.createElement('div');
                 element.appendChild(root);
+                roots.set(outputItem.id, root);
 
                 ReactDOM.render(
                     React.createElement(DataframeRenderer, {
                         context,
                         data,
                         metadata: dataFrameMetadata,
                         cellId,
                         cellIndex
                     }),
                     root
                 );
             } catch (error) {
                 console.error(`Error rendering dataframe: ${error}`);
                 const errorDiv = document.createElement('div');
                 errorDiv.style.padding = '10px';
                 errorDiv.style.color = 'var(--vscode-errorForeground)';
                 errorDiv.textContent = `Error rendering dataframe: ${error}`;
                 element.appendChild(errorDiv);
             }
         },
 
-        disposeOutputItem(_id?: string) {
-            // Cleanup if needed
+        disposeOutputItem(id?: string) {
+            if (id) {
+                const root = roots.get(id);
+                if (root) {
+                    ReactDOM.unmountComponentAtNode(root);
+                    roots.delete(id);
+                }
+            }
         }
     };
 };

Also applies to: 58-60

🤖 Prompt for AI Agents
In src/webviews/webview-side/dataframe-renderer/index.ts around lines 38-46 (and
similarly 58-60), ReactDOM.render mounts the DataframeRenderer into a DOM root
but never unmounts it, causing memory leaks when outputs are disposed; fix by
storing each root element (e.g., in a Map keyed by cellId or output id) at
render time and then calling ReactDOM.unmountComponentAtNode(root) inside
disposeOutputItem before removing the DOM node and deleting the Map entry so the
React component is properly cleaned up.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/kernels/execution/cellExecutionCreator.ts (1)

30-37: Align clearOutputOnStartWithTime naming and semantics
The code always clears outputs on first start but the JSDoc says “when start is called with a time.” Either:

  • Add && startTime !== undefined to the clear-output guard
  • Rename the param (and JSDoc) to reflect clear-on-first-start (e.g. clearOutputOnFirstStart)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fa78e2 and 3597ce3.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json (6 hunks)
  • src/kernels/execution/cellExecutionCreator.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/kernels/execution/cellExecutionCreator.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • src/kernels/execution/cellExecutionCreator.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecutionCreator.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/execution/cellExecutionCreator.ts
🧬 Code graph analysis (1)
src/kernels/execution/cellExecutionCreator.ts (2)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/platform/common/utils/misc.ts (1)
  • noop (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: copilot-setup-steps
🔇 Additional comments (3)
package.json (3)

2113-2113: Verify @deepnote/blocks v1.2.0 compatibility
Imports/usages in:
• src/notebooks/deepnote/deepnoteTypes.ts (DeepnoteBlock, DeepnoteFile)
• src/notebooks/deepnote/converters/textBlockConverter.ts (createMarkdown, stripMarkdown)
• src/kernels/execution/cellExecution.ts (createPythonCode)
Confirm no signature or behavior changes in these APIs and that metadata propagation remains intact.


2183-2183: Verify esbuild-postcss@0.0.4 compatibility with esbuild v0.25 & PostCSS 8

  • esbuild-postcss@0.0.4 hasn’t been updated in ~4 years and lacks official support for esbuild v0.25 or PostCSS 8—likely incompatible with Tailwind v4’s @tailwindcss/postcss plugin.
  • Test your esbuild v0.25 + postcss@8 + tailwindcss@4 + esbuild-postcss setup locally or switch to a maintained esbuild→PostCSS plugin (e.g., @chialab/esbuild-plugin-postcss).

1812-1822: Approve notebookRenderer contribution requiresMessaging "optional" is supported and build emits dist/webviews/webview-side/dataframeRenderer/dataframeRenderer.js as expected.

Comment on lines 63 to 76

// Clear outputs immediately on first start if configured to do so
// This ensures old outputs are removed before any new outputs arrive from the kernel
if (this.clearOutputOnStartWithTime) {
logger.trace(`Start cell ${this.cell.index} execution @ ${startTime} (clear output)`);
logger.trace(`Start cell ${this.cell.index} execution (clear output)`);
this._impl.clearOutput().then(noop, noop);
} else {
}

this._impl.start(startTime);
this._startTime = startTime;

if (startTime) {
logger.trace(`Start cell ${this.cell.index} execution @ ${startTime}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

clearOutput-before-start is not ordered; possible output flicker/loss

clearOutput() is async but start() is invoked immediately after. Kernel outputs may arrive before the clear resolves and be wiped, or logs will misrepresent ordering. Either:

  • Chain start inside the clearOutput promise (ensuring ordering), or
  • Adjust comments/logs to reflect that clear is only enqueued, and verify no flicker/loss in practice.

Recommend at least verifying under load.


🏁 Script executed:

#!/bin/bash
echo "=== clearOutputOnStartWithTime occurrences ==="
rg -n 'clearOutputOnStartWithTime' -n src/kernels/execution/cellExecutionCreator.ts
echo
echo "=== start() method and clearOutput usage ==="
rg -nP 'start\(' -C5 src/kernels/execution/cellExecutionCreator.ts
echo
echo "=== JSDoc blocks around start ==="
rg -nP '/\*\*' -C5 src/kernels/execution/cellExecutionCreator.ts

Length of output: 3692


🏁 Script executed:

#!/bin/bash
sed -n '1,45p' src/kernels/execution/cellExecutionCreator.ts

Length of output: 1494


Await clearOutput before start and align behavior with docs

  • clearOutput() is async and races with start() → outputs may be wiped after arriving; await or chain start in clearOutput.then().
  • JSDoc says “clear when start is called with a time” but code always clears when flag is true; adjust logic or update the comment.
🤖 Prompt for AI Agents
In src/kernels/execution/cellExecutionCreator.ts around lines 63-76, the code
currently calls clearOutput() without awaiting it and always clears when the
flag is true, which can race with start() and disagrees with the JSDoc; change
the logic so that clearing only happens when clearOutputOnStartWithTime is true
AND startTime is provided, and ensure start() is invoked only after
clearOutput() completes (chain or await the promise and handle errors with
noop). Update the trace logs accordingly so the "clear output" trace is emitted
when clearing occurs and the regular start trace after start() is called.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fa78e2 and 9fec8d4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • .github/workflows/ci.yml (1 hunks)
  • package.json (6 hunks)
  • src/kernels/execution/cellExecutionCreator.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/kernels/execution/cellExecutionCreator.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • src/kernels/execution/cellExecutionCreator.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecutionCreator.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/execution/cellExecutionCreator.ts
🧠 Learnings (1)
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Applied to files:

  • src/kernels/execution/cellExecutionCreator.ts
🧬 Code graph analysis (1)
src/kernels/execution/cellExecutionCreator.ts (2)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/platform/common/utils/misc.ts (1)
  • noop (11-11)
🔇 Additional comments (3)
package.json (3)

2184-2184: Remove esbuild-postcss compatibility warning
The build pipeline in build/esbuild/build.ts invokes PostCSS/Tailwind directly (postcss([tailwindcss, autoprefixer])) and does not use esbuild-postcss@0.0.4. The compatibility concern is not applicable.

Likely an incorrect or invalid review comment.


2113-2113: @deepnote/blocks bump Imports in src/kernels/execution/cellExecution.ts (createPythonCode), src/notebooks/deepnote/deepnoteTypes.ts (DeepnoteBlock/File), src/notebooks/deepnote/converters/textBlockConverter.ts (createMarkdown/stripMarkdown); confirm v1.2.0 introduces no breaking API changes to these.


1812-1822: Renderer entrypoint: confirm build output

  • requiresMessaging "optional" is valid per VS Code schema.
  • confirm that dist/webviews/webview-side/dataframeRenderer/dataframeRenderer.js is emitted by your build.

Comment on lines 63 to 69

// Clear outputs immediately on first start if configured to do so
// This ensures old outputs are removed before any new outputs arrive from the kernel
if (this.clearOutputOnStartWithTime) {
logger.trace(`Start cell ${this.cell.index} execution @ ${startTime} (clear output)`);
logger.trace(`Start cell ${this.cell.index} execution (clear output)`);
this._impl.clearOutput().then(noop, noop);
} else {
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Race condition: clearOutput not awaited before start.

clearOutput() returns a Thenable but isn't awaited, so _impl.start(startTime) on Line 71 may execute before outputs are actually cleared. Comment claims outputs are "removed before any new outputs arrive from the kernel," but implementation doesn't guarantee this ordering.

Consider making start() async or documenting that the clear is best-effort:

-    start(startTime?: number): void {
+    async start(startTime?: number): Promise<void> {
         // Allow this to be called more than once (so we can switch out a kernel during running a cell)
         if (!this.started) {
             this._started = true;

             // Clear outputs immediately on first start if configured to do so
             // This ensures old outputs are removed before any new outputs arrive from the kernel
             if (this.clearOutputOnStartWithTime) {
                 logger.trace(`Start cell ${this.cell.index} execution (clear output)`);
-                this._impl.clearOutput().then(noop, noop);
+                await this._impl.clearOutput();
             }

             this._impl.start(startTime);
             this._startTime = startTime;

             if (startTime) {
                 logger.trace(`Start cell ${this.cell.index} execution @ ${startTime}`);
             }
         }
     }

Note: This requires updating all callers to handle the async signature.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/kernels/execution/cellExecutionCreator.ts around lines 63 to 69,
clearOutput() is called but not awaited so _impl.start(startTime) can run before
outputs are cleared, violating the "removed before any new outputs arrive"
guarantee; update the method to await this._impl.clearOutput() before proceeding
(i.e., make the enclosing start path async and await clearOutput or await it
conditionally), and then propagate the async change by updating all callers to
handle the new Promise-based signature (or alternatively document the call as
best-effort if you cannot change all callers).

🧹 Nitpick | 🔵 Trivial

Promise lifecycle not tracked.

Per coding guidelines, "Monitor and dispose pending promises to prevent leaks during teardown." The clearOutput() promise is fire-and-forget with .then(noop, noop), and errors are silently swallowed.

If keeping synchronous signature, at minimum log errors:

             if (this.clearOutputOnStartWithTime) {
                 logger.trace(`Start cell ${this.cell.index} execution (clear output)`);
-                this._impl.clearOutput().then(noop, noop);
+                this._impl.clearOutput().then(
+                    noop,
+                    (err) => logger.warn(`Failed to clear output for cell ${this.cell.index}`, err)
+                );
             }
🤖 Prompt for AI Agents
In src/kernels/execution/cellExecutionCreator.ts around lines 63 to 69, the call
to this._impl.clearOutput() is fire-and-forget and swallows errors; capture the
returned promise, attach a rejection handler that logs the error (e.g.,
logger.error) instead of noop, and store the promise reference in a class-level
tracker (e.g., this._pendingPromises or this._clearOutputPromise) so it can be
awaited or cancelled during teardown/dispose to avoid leaks; update the
dispose/teardown logic to await or clear that tracked promise.

permissions:
id-token: write
contents: read
packages: read
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, we were not allowed to install the @deepnote/blocks package.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/notebooks/deepnote/deepnoteDataConverter.ts (1)

103-112: Make base64 conversion cross-runtime (Node + Web).

This issue was flagged in a previous review. atob only works in browsers; Node requires Buffer. The helper should detect runtime and support both.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fec8d4 and 9b9216a.

📒 Files selected for processing (5)
  • src/kernels/execution/cellExecutionCreator.ts (2 hunks)
  • src/kernels/execution/cellExecutionCreator.unit.test.ts (1 hunks)
  • src/kernels/execution/helpers.ts (10 hunks)
  • src/notebooks/controllers/vscodeNotebookController.ts (2 hunks)
  • src/notebooks/deepnote/deepnoteDataConverter.ts (9 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
  • src/kernels/execution/helpers.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/notebooks/deepnote/deepnoteDataConverter.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
  • src/kernels/execution/helpers.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
  • src/kernels/execution/helpers.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
  • src/kernels/execution/helpers.ts
src/notebooks/deepnote/**

📄 CodeRabbit inference engine (CLAUDE.md)

Deepnote integration code resides under src/notebooks/deepnote/

Files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
src/notebooks/deepnote/deepnoteDataConverter.ts

📄 CodeRabbit inference engine (CLAUDE.md)

deepnoteDataConverter.ts performs Deepnote data transformations

Files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
**/*.unit.test.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place unit tests in files matching *.unit.test.ts

**/*.unit.test.ts: Unit tests must use Mocha/Chai and have the .unit.test.ts extension
Place unit test files alongside the source files they test
Use assert.deepStrictEqual() for object comparisons in tests instead of checking individual properties

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

In unit tests, when a mock is returned from a promise, ensure the mocked instance has an undefined then property to avoid hanging tests

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
🧠 Learnings (8)
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Applied to files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/notebooks/controllers/vscodeNotebookController.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/**/*.ts : Monitor and dispose pending promises to prevent leaks during teardown

Applied to files:

  • src/kernels/execution/cellExecutionCreator.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : For new kernel sources, implement ILocalNotebookKernelSourceSelector (or similar)

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : Create a UI provider for kernel selection for the new kernel source

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/CellExecutionMessageHandler.ts : CellExecutionMessageHandler must process kernel messages (iopub, execute_reply) and update VS Code cell outputs

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-09-03T12:54:35.368Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/interactiveWindow.instructions.md:0-0
Timestamp: 2025-09-03T12:54:35.368Z
Learning: Applies to src/interactive-window/interactiveWindowController.ts : InteractiveWindowController must manage kernel selection/startup/lifecycle, system info cell handling, restart logic, and maintain owner file associations for kernel context

Applied to files:

  • src/notebooks/controllers/vscodeNotebookController.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteDataConverter.ts : deepnoteDataConverter.ts performs Deepnote data transformations

Applied to files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
📚 Learning: 2025-09-03T12:59:14.489Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/export/**/*.ts : Update FileConverter to handle the new export format

Applied to files:

  • src/notebooks/deepnote/deepnoteDataConverter.ts
🧬 Code graph analysis (5)
src/kernels/execution/cellExecutionCreator.ts (3)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/platform/common/utils/misc.ts (1)
  • noop (11-11)
src/notebooks/controllers/notebookIPyWidgetCoordinator.ts (1)
  • controller (27-32)
src/notebooks/controllers/vscodeNotebookController.ts (1)
src/kernels/types.ts (1)
  • IKernelController (930-933)
src/notebooks/deepnote/deepnoteDataConverter.ts (4)
src/notebooks/deepnote/pocket.ts (1)
  • addPocketToCellMetadata (20-42)
src/notebooks/deepnote/deepnoteSerializer.ts (1)
  • DeepnoteOutput (9-9)
src/notebooks/deepnote/deepnoteTypes.ts (1)
  • DeepnoteOutput (23-51)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • NotebookCellOutput (71-103)
  • NotebookCellOutputItem (16-69)
src/kernels/execution/cellExecutionCreator.unit.test.ts (2)
src/kernels/types.ts (1)
  • IKernelController (930-933)
src/kernels/execution/cellExecutionCreator.ts (2)
  • NotebookCellExecutionWrapper (22-121)
  • CellExecutionCreator (126-171)
src/kernels/execution/helpers.ts (1)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • NotebookCellOutput (71-103)
  • NotebookCellOutputItem (16-69)
🪛 GitHub Check: Lint & Format
src/kernels/execution/cellExecutionCreator.unit.test.ts

[failure] 234-234:
Unexpected empty method 'start'


[failure] 216-216:
Unexpected empty method 'start'


[failure] 186-186:
Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator


[failure] 169-169:
Unexpected empty method 'end'


[failure] 168-168:
Unexpected empty method 'start'


[failure] 128-128:
Unexpected empty method 'end'


[failure] 91-91:
Unexpected empty method 'end'


[failure] 45-45:
Unexpected empty method 'end'


[failure] 28-28:
Unexpected empty arrow function

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build & Test
🔇 Additional comments (13)
src/notebooks/controllers/vscodeNotebookController.ts (2)

563-563: LGTM - consistent refactoring.

Direct assignment of this.controller aligns with the removal of the KernelController wrapper at line 550. The subsequent controller reassignment logic (lines 574-579) remains sound.


550-550: Replace KernelController usages in interactive-window

In src/interactive-window/interactiveWindow.ts (import at line 55, instantiation at line 259) remove new KernelController(controller) and pass controller directly, confirming it implements IKernelController.

⛔ Skipped due to learnings
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/interactiveWindow.instructions.md:0-0
Timestamp: 2025-09-03T12:54:35.368Z
Learning: Applies to src/interactive-window/interactiveWindowController.ts : InteractiveWindowController must manage kernel selection/startup/lifecycle, system info cell handling, restart logic, and maintain owner file associations for kernel context
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/interactiveWindow.instructions.md:0-0
Timestamp: 2025-09-03T12:54:35.368Z
Learning: Applies to src/interactive-window/interactiveWindow.ts : InteractiveWindow should serve as the ViewModel for a single window, integrate with notebook documents, execute cells, provide debugging capabilities and kernel lifecycle management, and transform Python file code into notebook cells
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : For new kernel sources, implement ILocalNotebookKernelSourceSelector (or similar)
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/notebooks.instructions.md:0-0
Timestamp: 2025-09-03T12:59:14.489Z
Learning: Applies to src/notebooks/controllers/kernelSource/**/*.ts : Create a UI provider for kernel selection for the new kernel source
src/notebooks/deepnote/deepnoteDataConverter.ts (2)

31-31: LGTM!

The __hadOutputs flag correctly preserves round-trip fidelity by tracking whether a block originally had outputs, avoiding spurious empty arrays on round-trip. Passing index, block.id, and block.metadata to transformOutputsForVsCode enables proper per-cell metadata propagation.

Also applies to: 51-62


218-375: LGTM!

The signature expansion and consistent metadata construction across all output types (error, execute_result/display_data, stream, unknown, fallback) correctly propagates cellId, cellIndex, and blockMetadata to enable per-cell provenance tracking. The merge order (lines 302-314) correctly ensures output-specific metadata wins conflicts.

src/kernels/execution/helpers.ts (5)

118-176: LGTM: Provenance parameters threaded consistently.

The optional cellIndex, cellId, and cellMetadata parameters are properly typed and passed through the translation pipeline.


178-226: LGTM: Metadata merge strategy is sound.

The ordering (cellId → cellMetadata → cellIndex → output metadata) ensures output-specific metadata takes precedence. Dual storage at top level and in metadata.metadata serves the documented dual purpose.


240-288: LGTM: Translation functions consistently propagate provenance.

All output translators now accept and forward the optional provenance parameters to getOutputMetadata.


599-615: LGTM: Error output translation updated.

Provenance parameters correctly propagated through error translation path.


331-338: LGTM: Interface documents provenance fields.

cellIndex and cellId fields properly added to CellOutputMetadata.

src/kernels/execution/cellExecutionCreator.ts (2)

128-154: LGTM: Execution replacement handles API constraint correctly.

The logic properly ends existing executions (which can't be reused per VS Code API) and creates fresh wrappers. Auto-starting new executions when old ones were running maintains continuity during controller switches.


63-78: Document race risk & log clearOutput failures

  • Confirm/document that kernel outputs may arrive before clearOutput() completes and thus be cleared.
  • Replace .then(noop, noop) with .catch(ex => logger.warn(\Failed to clear cell ${this.cell.index} output on start`, ex))`.
src/kernels/execution/cellExecutionCreator.unit.test.ts (2)

1-195: LGTM: Comprehensive test coverage for execution lifecycle.

Tests properly validate start ordering, idempotency, and lifecycle transitions. Manual spies for call order tracking are appropriate.


197-277: LGTM: Re-execution test validates fresh wrapper creation.

Test correctly verifies that getOrCreate always ends old executions and creates fresh wrappers, with automatic start propagation when the old execution was running.

Comment on lines +86 to +97
// Convert VS Code outputs to Deepnote format
// Outputs are managed by VS Code natively, not stored in the pocket
// Only set outputs if the block originally had them (tracked by __hadOutputs flag)
const hadOutputs = cell.metadata?.__hadOutputs;
if (hadOutputs && !block.outputs && cell.outputs) {
block.outputs = this.transformOutputsForDeepnote(cell.outputs);
}

// Clean up internal tracking flags from metadata
if (block.metadata && '__hadOutputs' in block.metadata) {
delete block.metadata.__hadOutputs;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify __hadOutputs cleanup works correctly.

The code reads __hadOutputs from cell.metadata (line 89) but deletes it from block.metadata (line 95). This assumes createBlockFromPocket copies the flag from cell to block metadata. If it doesn't, the cleanup fails silently.

Run this script to verify the metadata flow:


🏁 Script executed:

#!/bin/bash
# Description: Verify createBlockFromPocket copies __hadOutputs to block.metadata

# Find createBlockFromPocket implementation
echo "=== createBlockFromPocket implementation ==="
ast-grep --pattern $'export function createBlockFromPocket($$$) {
  $$$
}'

# Check if __hadOutputs is a deepnoteBlockSpecificField that gets moved to pocket
echo -e "\n=== deepnoteBlockSpecificFields list ==="
rg -nP 'deepnoteBlockSpecificFields\s*=\s*\[' -A 10

# Find how block.metadata is populated from cell.metadata
echo -e "\n=== Block metadata assignment in createBlockFromPocket ==="
rg -nP 'block\.metadata\s*=' --type=ts -C 3 src/notebooks/deepnote/pocket.ts

Length of output: 1690


Add __hadOutputs to deepnoteBlockSpecificFields or delete from cell.metadata
__hadOutputs isn’t in deepnoteBlockSpecificFields (src/notebooks/deepnote/pocket.ts), so it’s never copied into block.metadata and delete block.metadata.__hadOutputs never runs. Either include __hadOutputs in the fields copied by createBlockFromPocket or remove the flag from cell.metadata instead.

🤖 Prompt for AI Agents
In src/notebooks/deepnote/deepnoteDataConverter.ts around lines 86 to 97, the
code checks and deletes block.metadata.__hadOutputs but that flag is never
copied into block.metadata because __hadOutputs is missing from
deepnoteBlockSpecificFields in src/notebooks/deepnote/pocket.ts; fix by either
(A) add "__hadOutputs" to deepnoteBlockSpecificFields so createBlockFromPocket
copies it into block.metadata (then the existing delete will work), or (B) if
__hadOutputs should remain only on the incoming cell, change this file to delete
the flag from cell.metadata instead of block.metadata (i.e., remove
block.metadata delete and delete cell.metadata.__hadOutputs after outputs
handling).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (8)
src/kernels/execution/cellExecutionCreator.ts (1)

59-81: Race condition resolved; start now precedes clearOutput.

The VS Code API requirement is satisfied (start before clearOutput), and error handling is improved with logging. However, since clearOutput is not awaited, the comment on Line 69 slightly overstates the guarantee.

Consider rewording Line 69:

-            // This ensures old outputs are removed before any new outputs arrive from the kernel
+            // This clears old outputs shortly after start; new kernel outputs may arrive before clearing completes
.github/workflows/ci.yml (1)

96-100: Correct: job-level permissions override defaults; keeping packages: read here is necessary.

Because the build job defines permissions, it overrides workflow-level ones. Without Line 99, packages: read would be dropped and npm install from GH Packages would fail. Your change is right.

Optionally:

  • Keep global packages: read for other jobs that run npm ci (simpler), and keep the build job override as-is; or
  • Drop the global permission and add packages: read to each job that installs deps (least-privilege).

Forks: installs will still fail on fork PRs (no repo secrets). If you need fork builds, use a PAT with read:packages or a proxy.

src/webviews/extension-side/dataframe/dataframeController.ts (5)

61-61: Still logging full message (data leak).

This logs the entire message payload, leaking table contents when command is copyTableData. Log only command + ids.

Based on past review comments.


88-115: Still missing cellIndex fallback.

Handler errors when cellId is absent or not found, ignoring message.cellIndex. Past comments flagged this.

Based on past review comments.


134-134: Still not checking applyEdit result.

applyEdit returns boolean. Past comments flagged this. Check result and bail on failure before re-executing.

Based on past review comments.


145-172: Still missing cellIndex fallback (goToPage).

Same issue as handleSelectPageSize: errors when cellId is absent or not found, ignoring message.cellIndex. Past comments flagged this.

Based on past review comments.


191-191: Still not checking applyEdit result (goToPage).

Same issue as handleSelectPageSize: applyEdit returns boolean that's ignored. Past comments flagged this.

Based on past review comments.

src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (1)

152-152: Fix localization parameterization.

Template literal interpolation breaks localization. Use parameterized form instead.

-                            title: l10n.t(`Init: "${initNotebook.name}"`),
+                            title: l10n.t('Init: "{0}"', initNotebook.name),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b9216a and e7cccb9.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml (1 hunks)
  • src/kernels/execution/cellExecutionCreator.ts (3 hunks)
  • src/kernels/execution/cellExecutionCreator.unit.test.ts (1 hunks)
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (3 hunks)
  • src/webviews/extension-side/dataframe/dataframeController.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.node.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.node.ts for Desktop-specific implementations that require full file system access and Python environments

Files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
src/notebooks/deepnote/**

📄 CodeRabbit inference engine (CLAUDE.md)

Deepnote integration code resides under src/notebooks/deepnote/

Files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
**/*.unit.test.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place unit tests in files matching *.unit.test.ts

**/*.unit.test.ts: Unit tests must use Mocha/Chai and have the .unit.test.ts extension
Place unit test files alongside the source files they test
Use assert.deepStrictEqual() for object comparisons in tests instead of checking individual properties

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

In unit tests, when a mock is returned from a promise, ensure the mocked instance has an undefined then property to avoid hanging tests

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
🧠 Learnings (8)
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use `l10n.t()` for user-facing strings

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
📚 Learning: 2025-10-14T14:15:55.496Z
Learnt from: Artmann
PR: deepnote/vscode-deepnote#28
File: src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts:93-99
Timestamp: 2025-10-14T14:15:55.496Z
Learning: For user Artmann in the deepnote/vscode-deepnote repository: avoid making nitpick or trivial-level review comments. Focus only on substantial issues like bugs, security concerns, performance problems, and significant architectural or design issues.

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
📚 Learning: 2025-10-01T20:18:36.836Z
Learnt from: saltenasl
PR: deepnote/vscode-deepnote#0
File: :0-0
Timestamp: 2025-10-01T20:18:36.836Z
Learning: For the repository deepnote/vscode-deepnote, user saltenasl prefers reviews without nitpicks or minor comments. Focus only on significant issues.

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteNotebookManager.ts : deepnoteNotebookManager.ts handles Deepnote notebook state management

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteActivationService.ts : deepnoteActivationService.ts manages VS Code activation for Deepnote

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Applied to files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/CellExecutionMessageHandler.ts : CellExecutionMessageHandler must process kernel messages (iopub, execute_reply) and update VS Code cell outputs

Applied to files:

  • src/kernels/execution/cellExecutionCreator.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/**/*.ts : Monitor and dispose pending promises to prevent leaks during teardown

Applied to files:

  • src/kernels/execution/cellExecutionCreator.ts
🧬 Code graph analysis (1)
src/kernels/execution/cellExecutionCreator.ts (2)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/platform/common/utils/misc.ts (1)
  • noop (11-11)
🔇 Additional comments (14)
src/kernels/execution/cellExecutionCreator.ts (2)

4-11: LGTM!

Type-only imports reduce bundle size and clarify intent.


131-157: LGTM! Fresh wrapper on every re-execution.

The logic correctly ends existing executions and creates fresh wrappers, preserving the started state. The comments clearly explain the behavior.

src/kernels/execution/cellExecutionCreator.unit.test.ts (8)

1-32: LGTM! Clean test setup.

Proper use of Mocha/Chai and ts-mockito. Setup initializes all mocks consistently.


33-62: LGTM! Validates start-before-clearOutput ordering.

Manual spy correctly verifies the fix for the race condition flagged in previous reviews.


64-76: LGTM! Negative test for clearOutputOnStartWithTime.

Confirms clearOutput is not called when the flag is false.


78-116: LGTM! Verifies startTime propagation and ordering.

Comprehensive test covering both parameter passing and call sequence.


118-153: LGTM! Confirms idempotent start().

Multiple start() calls correctly execute clearOutput and start only once.


155-163: LGTM! Validates started flag lifecycle.

Simple but essential test for state transitions.


165-206: LGTM! Tests clearOutput on reused execution.

Line 198 correctly uses void operator to handle the unhandled promise warning.


209-293: LGTM! Comprehensive re-execution test.

Validates the new getOrCreate behavior: old execution ended, fresh wrapper created and started, clearOutput called appropriately. Covers all assertions for the replacement logic.

src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (4)

2-9: LGTM!

Type-qualified imports and l10n addition are correct.


13-13: LGTM!

Type-only import is appropriate here.


145-145: LGTM!

Localization correctly applied to static progress title.


245-245: LGTM!

Progress calculation now correctly reaches 100% on the final block.

Comment on lines +19 to +42
type SelectPageSizeCommand = {
cellId?: string;
command: 'selectPageSize';
size: number;
};

type GoToPageCommand = {
cellId?: string;
command: 'goToPage';
page: number;
};

type CopyTableDataCommand = {
cellId?: string;
command: 'copyTableData';
data: string;
};

type ExportDataframeCommand = {
cellId?: string;
command: 'exportDataframe';
};

type DataframeCommand = SelectPageSizeCommand | GoToPageCommand | CopyTableDataCommand | ExportDataframeCommand;
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add cellIndex?: number to command types.

Past comments and handler logic indicate the renderer can send cellIndex as a fallback. Type definitions must include it to ensure type safety.

Apply this diff:

 type SelectPageSizeCommand = {
     cellId?: string;
+    cellIndex?: number;
     command: 'selectPageSize';
     size: number;
 };
 
 type GoToPageCommand = {
     cellId?: string;
+    cellIndex?: number;
     command: 'goToPage';
     page: number;
 };
 
 type CopyTableDataCommand = {
     cellId?: string;
+    cellIndex?: number;
     command: 'copyTableData';
     data: string;
 };
 
 type ExportDataframeCommand = {
     cellId?: string;
+    cellIndex?: number;
     command: 'exportDataframe';
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type SelectPageSizeCommand = {
cellId?: string;
command: 'selectPageSize';
size: number;
};
type GoToPageCommand = {
cellId?: string;
command: 'goToPage';
page: number;
};
type CopyTableDataCommand = {
cellId?: string;
command: 'copyTableData';
data: string;
};
type ExportDataframeCommand = {
cellId?: string;
command: 'exportDataframe';
};
type DataframeCommand = SelectPageSizeCommand | GoToPageCommand | CopyTableDataCommand | ExportDataframeCommand;
type SelectPageSizeCommand = {
cellId?: string;
cellIndex?: number;
command: 'selectPageSize';
size: number;
};
type GoToPageCommand = {
cellId?: string;
cellIndex?: number;
command: 'goToPage';
page: number;
};
type CopyTableDataCommand = {
cellId?: string;
cellIndex?: number;
command: 'copyTableData';
data: string;
};
type ExportDataframeCommand = {
cellId?: string;
cellIndex?: number;
command: 'exportDataframe';
};
type DataframeCommand =
| SelectPageSizeCommand
| GoToPageCommand
| CopyTableDataCommand
| ExportDataframeCommand;
🤖 Prompt for AI Agents
In src/webviews/extension-side/dataframe/dataframeController.ts around lines 19
to 42, the command type definitions are missing the optional cellIndex property
that the renderer may send as a fallback; update each command type
(SelectPageSizeCommand, GoToPageCommand, CopyTableDataCommand,
ExportDataframeCommand) to include cellIndex?: number and ensure the
DataframeCommand union reflects those updated types so the handlers accept
cellIndex without type errors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to rely on cellId instead of cellIndex.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Artmann, understood. The current types correctly reflect that design—cellId only, no cellIndex fallback. I'll retract my suggestion.


✏️ Learnings added
Learnt from: Artmann
PR: deepnote/vscode-deepnote#28
File: src/webviews/extension-side/dataframe/dataframeController.ts:19-42
Timestamp: 2025-10-14T16:18:46.584Z
Learning: In the vscode-deepnote codebase, for dataframe renderer commands (SelectPageSizeCommand, GoToPageCommand, CopyTableDataCommand, ExportDataframeCommand), use cellId as the cell identifier and do not add cellIndex as a fallback.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +75 to +82
if (message.command === 'copyTableData') {
// TODO: Implement dataframe export functionality
return;
}

if (message.command === 'exportDataframe') {
// TODO: Implement dataframe export functionality
return;
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Complete export/copy stubs.

These TODOs block dataframe export and clipboard features.

Do you want me to generate implementations or open tracking issues?

🤖 Prompt for AI Agents
In src/webviews/extension-side/dataframe/dataframeController.ts around lines 75
to 82, the two TODO stubs for message.command === 'copyTableData' and
'exportDataframe' leave clipboard/export functionality unimplemented; replace
them with handlers that validate the incoming payload (e.g., ensure
message.payload.rows/columns or message.payload.data and optional format),
transform the data into the requested export format (CSV/JSON/TSV) using
existing util functions or a small serializer, and then either copy the
serialized string to the clipboard (use the VS Code webview API or a provided
copy util) for 'copyTableData' or trigger a file export flow for
'exportDataframe' (post a message back to the extension host with the serialized
data and filename or call the existing export helper). Ensure both handlers
catch errors, log or post error responses back to the caller, and send success
confirmations (so the UI can show success/failure).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do them later. Don't worry about it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Artmann, understood. I'll skip these TODOs.

</button>
</div>

<div>{/* Actions */}</div>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do these next.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (7)
src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (1)

22-22: Class still missing interface declaration.

The class does not implement IDeepnoteInitNotebookRunner despite the interface being defined at line 292.

Apply this diff:

 @injectable()
-export class DeepnoteInitNotebookRunner {
+export class DeepnoteInitNotebookRunner implements IDeepnoteInitNotebookRunner {
.github/workflows/ci.yml (1)

13-17: Deduplicate packages: read; adopt one strategy.

Pick one:

  • Least‑privilege: remove global packages: read and grant it only to jobs that install from GH Packages (lint, typecheck, build, check_licenses, audits).
  • Or keep global and avoid per‑job overrides, but build needs a job block (id‑token), so you’d still need packages: read there.

Also, if you expect fork PRs to build, GITHUB_TOKEN cannot read private GH Packages; use a PAT with read:packages or a proxy.

Also applies to: 96-100

src/webviews/extension-side/dataframe/dataframeController.ts (4)

88-143: Add cellIndex fallback for selectPageSize.

The handler requires cellId and errors if not found, but the renderer may send only cellIndex (common before metadata includes id). Accept and validate cellIndex as a fallback to avoid breaking pagination.

Apply this diff:

-        if (!message.cellId) {
-            const errorMessage = l10n.t(
-                'Unable to update page size: No cell identifier provided. Please re-run the cell to update the output metadata.'
-            );
-
-            logger.error(`[DataframeController] ${errorMessage}`);
-
-            await window.showErrorMessage(errorMessage);
-
-            throw new Error(errorMessage);
-        }
-
         const cells = editor.notebook.getCells();
-        const cell = cells.find((c) => c.metadata.id === message.cellId);
-
-        if (!cell) {
-            const errorMessage = l10n.t(
-                'Unable to update page size: Could not find the cell with ID {0}. The cell may have been deleted.',
-                message.cellId ?? ''
-            );
-
-            logger.error(`[DataframeController] ${errorMessage}`);
-
-            await window.showErrorMessage(errorMessage);
-
-            throw new Error(errorMessage);
-        }
-
-        const cellIndex = cell.index;
+        let cell = message.cellId ? cells.find((c) => c.metadata.id === message.cellId) : undefined;
+        let cellIndex = cell?.index;
+
+        // Fallback to cellIndex if cellId lookup fails
+        if (!cell && typeof message.cellIndex === 'number') {
+            const idx = message.cellIndex;
+            if (idx >= 0 && idx < editor.notebook.cellCount) {
+                cell = editor.notebook.cellAt(idx);
+                cellIndex = idx;
+                logger.debug?.(`[DataframeController] selectPageSize: using cellIndex fallback ${idx}`);
+            }
+        }
+
+        if (!cell || cellIndex === undefined) {
+            const errorMessage = l10n.t(
+                'Unable to update page size: Could not resolve cell by id ({0}) or index ({1}).',
+                message.cellId ?? 'n/a',
+                (message as any).cellIndex ?? 'n/a'
+            );
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }

Note: Add cellIndex?: number to SelectPageSizeCommand type definition.


134-134: Check applyEdit result before re-execution.

applyEdit returns a boolean. If it fails, re-executing the cell is wasteful and may confuse users.

Apply this diff:

-        await workspace.applyEdit(edit);
+        const success = await workspace.applyEdit(edit);
+        if (!success) {
+            const errorMessage = l10n.t('Failed to update cell metadata.');
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }

145-200: Add cellIndex fallback for goToPage.

Like handleSelectPageSize, this handler should accept cellIndex as a fallback when cellId is absent or not found. The renderer may send only the index.

Apply this diff:

-        if (!message.cellId) {
-            const errorMessage = l10n.t(
-                'Unable to navigate to page: No cell identifier provided. Please re-run the cell to update the output metadata.'
-            );
-
-            logger.error(`[DataframeController] ${errorMessage}`);
-
-            await window.showErrorMessage(errorMessage);
-
-            throw new Error(errorMessage);
-        }
-
         const cells = editor.notebook.getCells();
-        const cell = cells.find((c) => c.metadata.id === message.cellId);
-
-        if (!cell) {
-            const errorMessage = l10n.t(
-                'Unable to navigate to page: Could not find the cell with ID {0}. The cell may have been deleted.',
-                message.cellId ?? ''
-            );
-
-            logger.error(`[DataframeController] ${errorMessage}`);
-
-            await window.showErrorMessage(errorMessage);
-
-            throw new Error(errorMessage);
-        }
-
-        // Update page index in table state within cell metadata
+        let cell = message.cellId ? cells.find((c) => c.metadata.id === message.cellId) : undefined;
+        let cellIndex = cell?.index;
+
+        // Fallback to cellIndex if cellId lookup fails
+        if (!cell && typeof message.cellIndex === 'number') {
+            const idx = message.cellIndex;
+            if (idx >= 0 && idx < editor.notebook.cellCount) {
+                cell = editor.notebook.cellAt(idx);
+                cellIndex = idx;
+                logger.debug?.(`[DataframeController] goToPage: using cellIndex fallback ${idx}`);
+            }
+        }
+
+        if (!cell || cellIndex === undefined) {
+            const errorMessage = l10n.t(
+                'Unable to navigate to page: Could not resolve cell by id ({0}) or index ({1}).',
+                message.cellId ?? 'n/a',
+                (message as any).cellIndex ?? 'n/a'
+            );
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }
+
         const existingTableState = cell.metadata.deepnote_table_state || {};
         const updatedTableState = {
             ...existingTableState,
             pageIndex: message.page
         };
 
-        const cellIndex = cell.index;
-
         const edit = new WorkspaceEdit();

Note: Add cellIndex?: number to GoToPageCommand type definition.


191-191: Check applyEdit result before re-execution.

Same as handleSelectPageSize: check the boolean result and bail if the edit fails.

Apply this diff:

-        await workspace.applyEdit(edit);
+        const success = await workspace.applyEdit(edit);
+        if (!success) {
+            const errorMessage = l10n.t('Failed to update cell metadata.');
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }
src/kernels/execution/cellExecutionCreator.ts (1)

30-37: JSDoc mismatch: behavior ignores “with a time” qualifier

Docs say “clear the output when start is called with a time,” but the code clears whenever the flag is true, regardless of startTime.

Align either the code or the doc:

  • Code path: require startTime be provided:
- if (this.clearOutputOnStartWithTime) {
+ if (this.clearOutputOnStartWithTime && typeof startTime === 'number') {
  • Or update the JSDoc to drop the “with a time” condition.

Which behavior do we want? I can update tests accordingly.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b9216a and e7cccb9.

📒 Files selected for processing (5)
  • .github/workflows/ci.yml (1 hunks)
  • src/kernels/execution/cellExecutionCreator.ts (3 hunks)
  • src/kernels/execution/cellExecutionCreator.unit.test.ts (1 hunks)
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (3 hunks)
  • src/webviews/extension-side/dataframe/dataframeController.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
**/*.node.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use *.node.ts for Desktop-specific implementations that require full file system access and Python environments

Files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
src/notebooks/deepnote/**

📄 CodeRabbit inference engine (CLAUDE.md)

Deepnote integration code resides under src/notebooks/deepnote/

Files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
**/*.unit.test.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place unit tests in files matching *.unit.test.ts

**/*.unit.test.ts: Unit tests must use Mocha/Chai and have the .unit.test.ts extension
Place unit test files alongside the source files they test
Use assert.deepStrictEqual() for object comparisons in tests instead of checking individual properties

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

In unit tests, when a mock is returned from a promise, ensure the mocked instance has an undefined then property to avoid hanging tests

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
🧠 Learnings (7)
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Applied to files:

  • src/kernels/execution/cellExecutionCreator.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
📚 Learning: 2025-09-03T12:58:18.091Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/**/*.ts : Monitor and dispose pending promises to prevent leaks during teardown

Applied to files:

  • src/kernels/execution/cellExecutionCreator.ts
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use `l10n.t()` for user-facing strings

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
  • src/webviews/extension-side/dataframe/dataframeController.ts
📚 Learning: 2025-10-14T14:15:55.496Z
Learnt from: Artmann
PR: deepnote/vscode-deepnote#28
File: src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts:93-99
Timestamp: 2025-10-14T14:15:55.496Z
Learning: For user Artmann in the deepnote/vscode-deepnote repository: avoid making nitpick or trivial-level review comments. Focus only on substantial issues like bugs, security concerns, performance problems, and significant architectural or design issues.

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
📚 Learning: 2025-10-01T20:18:36.836Z
Learnt from: saltenasl
PR: deepnote/vscode-deepnote#0
File: :0-0
Timestamp: 2025-10-01T20:18:36.836Z
Learning: For the repository deepnote/vscode-deepnote, user saltenasl prefers reviews without nitpicks or minor comments. Focus only on significant issues.

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteNotebookManager.ts : deepnoteNotebookManager.ts handles Deepnote notebook state management

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to src/notebooks/deepnote/deepnoteActivationService.ts : deepnoteActivationService.ts manages VS Code activation for Deepnote

Applied to files:

  • src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts
🧬 Code graph analysis (3)
src/kernels/execution/cellExecutionCreator.ts (3)
src/platform/logging/index.ts (1)
  • logger (35-48)
src/platform/common/utils/misc.ts (1)
  • noop (11-11)
src/notebooks/controllers/notebookIPyWidgetCoordinator.ts (1)
  • controller (27-32)
src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (1)
src/test/datascience/ipywidgets/samples/beakerx/extension.js (1)
  • codeBlocks (20949-20949)
src/kernels/execution/cellExecutionCreator.unit.test.ts (3)
src/kernels/types.ts (1)
  • IKernelController (930-933)
src/test/mocks/vsc/index.ts (1)
  • CancellationToken (100-112)
src/kernels/execution/cellExecutionCreator.ts (2)
  • NotebookCellExecutionWrapper (22-124)
  • CellExecutionCreator (129-174)
🔇 Additional comments (5)
src/notebooks/deepnote/deepnoteInitNotebookRunner.node.ts (3)

2-9: LGTM.

Type-only imports and l10n addition are correct.


145-145: LGTM.

Progress titles correctly localized.

Also applies to: 152-152


245-245: LGTM.

Progress now reaches 100% on the final block.

.github/workflows/ci.yml (1)

96-100: Correct: job-level packages: read is needed here.

Once a job defines its own permissions, it overrides global. Adding packages: read to build is required for installing @deepnote/blocks.

src/kernels/execution/cellExecutionCreator.unit.test.ts (1)

188-206: Nice: explicit void on unawaited promise

The linter warning is addressed with void wrapper.clearOutput();. LGTM.

Comment on lines +64 to +76
// Must call start() before clearOutput() per VS Code API requirements
this._impl.start(startTime);
this._startTime = startTime;
// We clear the output as soon as we start,
// We generally start with a time, when we receive a response from the kernel,
// indicating the fact that the kernel has started processing the output.
// That's when we clear the output. (ideally it should be cleared as soon as its queued, but thats an upstream core issue).

// Clear outputs immediately after start if configured to do so
// This ensures old outputs are removed before any new outputs arrive from the kernel
if (this.clearOutputOnStartWithTime) {
logger.trace(`Start cell ${this.cell.index} execution @ ${startTime} (clear output)`);
this._impl.clearOutput().then(noop, noop);
} else {
logger.trace(`Start cell ${this.cell.index} execution (clear output)`);

this._impl
.clearOutput()
.then(noop, (err) => logger.warn(`Failed to clear output for cell ${this.cell.index}`, err));
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Race persists: clearOutput is not awaited; output loss/flicker possible

Start is called, then clearOutput runs fire-and-forget. Kernel/user outputs may append before clear completes and be wiped. The comment “ensures old outputs are removed before any new outputs arrive” is not guaranteed.

Fix by tracking the clear promise and gating subsequent output writes on it.

Apply these changes:

  1. Add a private tracker:
 export class NotebookCellExecutionWrapper implements NotebookCellExecution {
   public _started: boolean = false;
   private _startTime?: number;
+  private _clearOutputPromise?: Promise<void>;
   public errorInfo: CellExecutionError;
  1. Capture and handle the clear promise in start():
   if (this.clearOutputOnStartWithTime) {
     logger.trace(`Start cell ${this.cell.index} execution (clear output)`);
-    this._impl
-        .clearOutput()
-        .then(noop, (err) => logger.warn(`Failed to clear output for cell ${this.cell.index}`, err));
+    this._clearOutputPromise = this._impl.clearOutput().catch((err) => {
+      logger.warn(`Failed to clear output for cell ${this.cell.index}`, err);
+    }).finally(() => {
+      this._clearOutputPromise = undefined;
+    });
   }
  1. Gate output methods on the pending clear:
   clearOutput(cell?: NotebookCell): Thenable<void> {
     this.startIfNecessary();
-    return this._impl.clearOutput(cell);
+    const run = () => this._impl.clearOutput(cell);
+    return this._clearOutputPromise ? this._clearOutputPromise.then(run) : run();
   }
   replaceOutput(out: NotebookCellOutput | NotebookCellOutput[], cell?: NotebookCell): Thenable<void> {
     this.startIfNecessary();
-    return this._impl.replaceOutput(out, cell);
+    const run = () => this._impl.replaceOutput(out, cell);
+    return this._clearOutputPromise ? this._clearOutputPromise.then(run) : run();
   }
   appendOutput(out: NotebookCellOutput | NotebookCellOutput[], cell?: NotebookCell): Thenable<void> {
     this.startIfNecessary();
-    return this._impl.appendOutput(out, cell);
+    const run = () => this._impl.appendOutput(out, cell);
+    return this._clearOutputPromise ? this._clearOutputPromise.then(run) : run();
   }
   replaceOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], output: NotebookCellOutput): Thenable<void> {
     this.startIfNecessary();
-    return this._impl.replaceOutputItems(items, output);
+    const run = () => this._impl.replaceOutputItems(items, output);
+    return this._clearOutputPromise ? this._clearOutputPromise.then(run) : run();
   }
   appendOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], output: NotebookCellOutput): Thenable<void> {
     this.startIfNecessary();
-    return this._impl.appendOutputItems(items, output);
+    const run = () => this._impl.appendOutputItems(items, output);
+    return this._clearOutputPromise ? this._clearOutputPromise.then(run) : run();
   }

Optionally, make start() async and await the clear, but the above preserves the sync signature while ensuring ordering for all output writes. Based on learnings

Would you like me to add a focused test that simulates clearOutput latency and asserts appendOutput is gated?


🏁 Script executed:

#!/bin/bash
set -e
file=$(fd --full-path 'cellExecutionCreator.ts' | head -n 1)
echo "Inspecting: $file"
echo "=== clearOutputOnStartWithTime context ==="
rg -n -C2 'clearOutputOnStartWithTime' "$file"
echo
echo "=== start(...) method snippet ==="
rg -n -C3 'start\(' "$file"
echo
echo "=== output methods ==="
rg -n -C1 -e 'appendOutput' -e 'replaceOutput' -e 'clearOutput(' "$file"
echo
echo "=== getOrCreate occurrences ==="
rg -n 'getOrCreate' -g '*.ts'

Length of output: 3739


Track clearOutput() promise to avoid races
The fire-and-forget clear can wipe new outputs. Introduce a private _clearOutputPromise in NotebookCellExecutionWrapper and gate all output methods (clearOutput, replaceOutput, appendOutput, etc.) on its resolution.

Align JSDoc with behavior
Update the clearOutputOnStartWithTime param doc to reflect that clearing happens on every start(), not only when startTime is provided.

Remove auto-start in getOrCreate
Current logic immediately calls start() on a new wrapper if the previous execution was started, bypassing CellExecutionQueue. Defer or remove this auto-start so the queue orchestrates executions.

🤖 Prompt for AI Agents
In src/kernels/execution/cellExecutionCreator.ts around lines 64-76: the current
fire-and-forget call to this._impl.clearOutput() can race and wipe new outputs,
the JSDoc for clearOutputOnStartWithTime is inaccurate, and getOrCreate
auto-starts a new wrapper bypassing the execution queue. Fix by adding a private
_clearOutputPromise on NotebookCellExecutionWrapper and assign it when
clearOutput() is invoked (set to the clearOutput() promise and clear it on
settle); update all output-modifying methods (clearOutput, replaceOutput,
appendOutput, etc.) to wait for or chain off _clearOutputPromise before
performing their actions; update the JSDoc for clearOutputOnStartWithTime to
state clearing occurs on every start() call (not only when startTime provided);
and remove or defer the automatic start() call in getOrCreate so a newly created
wrapper is not started implicitly but instead left for the CellExecutionQueue to
orchestrate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll live with this for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Artmann, understood. I've noted your decision to defer this fix for now.


🧠 Learnings used
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/CellExecutionMessageHandler.ts : CellExecutionMessageHandler must process kernel messages (iopub, execute_reply) and update VS Code cell outputs

Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/kernelProvider.base.ts : KernelProviderBase must create/manage IKernel instances, track by NotebookDocument, manage lifecycle (start/restart/dispose), and expose INotebookKernelExecution

Comment on lines +68 to +76
// Clear outputs immediately after start if configured to do so
// This ensures old outputs are removed before any new outputs arrive from the kernel
if (this.clearOutputOnStartWithTime) {
logger.trace(`Start cell ${this.cell.index} execution @ ${startTime} (clear output)`);
this._impl.clearOutput().then(noop, noop);
} else {
logger.trace(`Start cell ${this.cell.index} execution (clear output)`);

this._impl
.clearOutput()
.then(noop, (err) => logger.warn(`Failed to clear output for cell ${this.cell.index}`, err));
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Clarify API order requirement and guarantee language

The comment asserts VS Code API requires start() before clearOutput(). Please verify and adjust documentation if needed. Also weaken “ensures old outputs are removed…” since ordering isn’t guaranteed without gating.


🌐 Web query:

Does VS Code's NotebookCellExecution API require calling start() before clearOutput(), and are output methods valid prior to start()?

💡 Result:

Short answer: No — start() is not required. clearOutput(), append/replaceOutput(Items) and similar output methods can be called on a NotebookCellExecution before you call start(); start() only changes the execution state (Pending → Executing) and the running clock/visual state. The API docs describe the lifecycle (execution is created in Pending; start(...) moves it to Executing) but do not state that output methods are invalid until start() is called. [1][2]

Sources:

  • NotebookCellExecution API (methods: clearOutput, appendOutput, replaceOutput; lifecycle description). [1]
  • NotebookController / createNotebookCellExecution reference (creates a NotebookCellExecution object; start changes state). [2]

Update comment to reflect API semantics

  • clearOutput() can be called before start(); start() only transitions state
  • Change “ensures old outputs are removed before any new outputs arrive from the kernel” to “attempts to remove old outputs before new ones arrive”

Location: src/kernels/execution/cellExecutionCreator.ts (lines 68–76)

🤖 Prompt for AI Agents
In src/kernels/execution/cellExecutionCreator.ts around lines 68 to 76, update
the inline comment to reflect API semantics: change the sentence that reads
“ensures old outputs are removed before any new outputs arrive from the kernel”
to a more accurate “attempts to remove old outputs before new ones arrive,” and
note that clearOutput() may be called before start() because start() only
transitions state; keep the surrounding logging and promise handling as-is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll live with this for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Artmann, understood! I've noted your decision.

🐰💭
(Respecting your call)


🧠 Learnings used
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/CellExecutionMessageHandler.ts : CellExecutionMessageHandler must process kernel messages (iopub, execute_reply) and update VS Code cell outputs

Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/kernelProvider.base.ts : KernelProviderBase must create/manage IKernel instances, track by NotebookDocument, manage lifecycle (start/restart/dispose), and expose INotebookKernelExecution

Comment on lines +146 to +151
// If the old execution was started, start the new one immediately
// This handles the case where we're switching controllers mid-execution
if (wasStarted) {
cellExecution.start(new Date().getTime());
}

Copy link
Contributor

@coderabbitai coderabbitai bot Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Avoid auto-starting in getOrCreate; let the CellExecutionQueue own start()

Auto-starting the new wrapper when the old one “wasStarted” bypasses the queue and can violate “enqueue via CellExecutionQueue” guidance. Also risks overlapping starts during re-exec flows.

Suggestion:

  • Don’t call start() here. Return the fresh wrapper and let the queue/runner decide when to start.
  • If needed, pass a hint back to the caller (e.g., wasPreviouslyStarted) to inform scheduling.
-            if (wasStarted) {
-                cellExecution.start(new Date().getTime());
-            }

Based on learnings

Confirm that re-exec paths (e.g., dataframe pagination) enqueue the cell instead of relying on auto-start here.


🏁 Script executed:

#!/bin/bash
set -e
grep -n "getOrCreate" src/kernels/execution/cellExecutionCreator.ts
sed -n '1,200p' src/kernels/execution/cellExecutionCreator.ts

Length of output: 6715


🏁 Script executed:

#!/bin/bash
set -e
grep -R "CellExecutionQueue" -n src

Length of output: 1603


🏁 Script executed:

#!/bin/bash
set -e
sed -n '1,300p' src/kernels/execution/cellExecutionQueue.ts

Length of output: 14264


🏁 Script executed:

#!/bin/bash
set -e
grep -n "class CellExecutionFactory" -n src/kernels/execution/cellExecution.ts
sed -n '1,200p' src/kernels/execution/cellExecution.ts

Length of output: 9191


Remove auto-start from getOrCreate

Delete this block in src/kernels/execution/cellExecutionCreator.ts (lines 146–151):

-            // If the old execution was started, start the new one immediately
-            if (wasStarted) {
-                cellExecution.start(new Date().getTime());
-            }

All starts must go through CellExecutionQueue; it will enqueue and invoke start() in order.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// If the old execution was started, start the new one immediately
// This handles the case where we're switching controllers mid-execution
if (wasStarted) {
cellExecution.start(new Date().getTime());
}
🤖 Prompt for AI Agents
In src/kernels/execution/cellExecutionCreator.ts around lines 146 to 151, remove
the block that calls cellExecution.start() when wasStarted is true so
getOrCreate does not auto-start executions; ensure getOrCreate only returns the
execution instance and that any logic relying on the previous auto-start is
moved to CellExecutionQueue (which should be responsible for enqueuing and
invoking start() in order).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks the outputs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

@Artmann, understood—I'll investigate what breaks when we remove that auto-start. Let me check the execution flow to see where start() is typically called and identify which scenario depends on this.


🏁 Script executed:

#!/bin/bash
# Find all places where getOrCreate is called and check if they call start()
rg -nP -A5 -B2 'getOrCreate\(' --type=ts src/kernels/execution/

# Check how notebook.cell.execute command flows into execution
rg -nP 'executeCommand.*notebook\.cell\.execute' --type=ts src/

# Look for controller switching logic
rg -nP 'controllerId.*!==|switchController|controller.*switch' --type=ts src/kernels/

Length of output: 7291


@Artmann, you're absolutely right—my suggestion was incorrect. After analyzing the execution flow, I see why the auto-start is necessary:

Re-execution scenario (e.g., dataframe pagination):

  1. Cell is already executing and displaying outputs (execution1 is started)
  2. User changes page size → triggers re-execution via notebook.cell.execute command
  3. getOrCreate ends execution1 and creates fresh execution2
  4. Without auto-start: execution2 never starts → no new outputs generated → broken

The auto-start specifically handles re-execution of already-running cells, which is distinct from initial cell execution that goes through the queue. The test even confirms this with the comment "Simulate re-execution (like what happens when pageSize changes in dataframe)".

The current implementation is correct for supporting re-execution flows. My apologies for the confusion.

Is there a different concern about the execution handling that I should address instead?


🧬 Code Graph Analysis Results

[src/platform/common/utils/misc.ts:11-11]

export function noop() {}

[src/test/mocks/vsc/extHostedTypes.ts:71-103]

export class NotebookCellOutput {
static isNotebookCellOutput(candidate: any): candidate is vscode.NotebookCellOutput {
if (candidate instanceof NotebookCellOutput) {
return true;
}
if (!candidate || typeof candidate !== 'object') {
return false;
}
return (
typeof (candidate).id === 'string' &&
Array.isArray((candidate).items)
);
}

    id: string;
    items: NotebookCellOutputItem[];
    metadata?: Record<string, any>;

    constructor(
        items: NotebookCellOutputItem[],
        idOrMetadata?: string | Record<string, any>,
        metadata?: Record<string, any>
    ) {
        this.items = items;
        if (typeof idOrMetadata === 'string') {
            this.id = idOrMetadata;
            this.metadata = metadata;
        } else {
            this.id = generateUuid();
            this.metadata = idOrMetadata ?? metadata;
        }
    }
}

[src/test/mocks/vsc/extHostedTypes.ts:16-69]

export class NotebookCellOutputItem {
static isNotebookCellOutputItem(obj: unknown): obj is vscode.NotebookCellOutputItem {
if (obj instanceof NotebookCellOutputItem) {
return true;
}
if (!obj) {
return false;
}
return (
typeof (<vscode.NotebookCellOutputItem>obj).mime === 'string' &&
(<vscode.NotebookCellOutputItem>obj).data instanceof Uint8Array
);
}

    static error(err: Error | { name: string; message?: string; stack?: string }): NotebookCellOutputItem {
        const obj = {
            name: err.name,
            message: err.message,
            stack: err.stack
        };
        return NotebookCellOutputItem.json(obj, 'application/vnd.code.notebook.error');
    }

    static stdout(value: string): NotebookCellOutputItem {
        return NotebookCellOutputItem.text(value, 'application/vnd.code.notebook.stdout');
    }

    static stderr(value: string): NotebookCellOutputItem {
        return NotebookCellOutputItem.text(value, 'application/vnd.code.notebook.stderr');
    }

    static bytes(value: Uint8Array, mime: string = 'application/octet-stream'): NotebookCellOutputItem {
        return new NotebookCellOutputItem(value, mime);
    }

    static #encoder = new TextEncoder();

    static text(value: string, mime: string = 'text/plain'): NotebookCellOutputItem {
        const bytes = NotebookCellOutputItem.#encoder.encode(String(value));
        return new NotebookCellOutputItem(bytes, mime);
    }

    static json(value: any, mime: string = 'text/x-json'): NotebookCellOutputItem {
        const rawStr = JSON.stringify(value, undefined, '\t');
        return NotebookCellOutputItem.text(rawStr, mime);
    }

    constructor(
        public data: Uint8Array,
        public mime: string
    ) {
        this.mime = mime;
    }
}

[src/kernels/types.ts:930-933]

export interface IKernelController {
id: string;
createNotebookCellExecution(cell: NotebookCell): NotebookCellExecution;
}

[src/kernels/execution/codeExecution.ts:42-44]

public get result(): Promise {
return this._done.promise;
}

[src/kernels/execution/cellExecution.ts:69-71]

public get result(): Promise {
return this._result.promise;
}


⛔ Learnings removed
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

🧠 Learnings used
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/instructions/kernel.instructions.md:0-0
Timestamp: 2025-09-03T12:58:18.091Z
Learning: Applies to src/kernels/execution/**/*.ts : Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/webviews/extension-side/dataframe/dataframeController.ts (2)

191-199: Check applyEdit result before re-execution.

Same issue as handleSelectPageSize: if applyEdit fails, re-executing is incorrect.

Apply this diff:

-        await workspace.applyEdit(edit);
+        const success = await workspace.applyEdit(edit);
+        if (!success) {
+            const errorMessage = l10n.t('Failed to update cell metadata for page navigation.');
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }
 
         // Re-execute the cell to apply the new page

134-142: Check applyEdit result before re-execution.

applyEdit returns a boolean. If it fails, re-executing with stale metadata is pointless and confusing.

Apply this diff:

-        await workspace.applyEdit(edit);
+        const success = await workspace.applyEdit(edit);
+        if (!success) {
+            const errorMessage = l10n.t('Failed to update cell metadata for page size change.');
+            logger.error(`[DataframeController] ${errorMessage}`);
+            await window.showErrorMessage(errorMessage);
+            throw new Error(errorMessage);
+        }
 
         // Re-execute the cell to apply the new page size
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7cccb9 and 8309267.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml (1 hunks)
  • src/kernels/execution/cellExecutionCreator.unit.test.ts (1 hunks)
  • src/webviews/extension-side/dataframe/dataframeController.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/!(*.node|*.web).ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place shared cross-platform logic in common .ts files (not suffixed with .node or .web)

Files:

  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Inject interfaces, not concrete classes
Avoid circular dependencies
Use l10n.t() for user-facing strings
Use typed error classes from src/platform/errors/ when throwing or handling errors
Use the ILogger service instead of console.log
Preserve error details while scrubbing PII in messages and telemetry
Include the Microsoft copyright header in source files
Prefer async/await and handle cancellation with CancellationToken

**/*.{ts,tsx}: Order class members (methods, fields, properties) first by accessibility (public/protected/private) and then alphabetically
Do not add the Microsoft copyright header to new files
Use Uri.joinPath() to construct file paths instead of manual string concatenation
Add a blank line after groups of const declarations and before return statements for readability
Separate third-party imports from local file imports

Files:

  • src/webviews/extension-side/dataframe/dataframeController.ts
  • src/kernels/execution/cellExecutionCreator.unit.test.ts
**/*.unit.test.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Place unit tests in files matching *.unit.test.ts

**/*.unit.test.ts: Unit tests must use Mocha/Chai and have the .unit.test.ts extension
Place unit test files alongside the source files they test
Use assert.deepStrictEqual() for object comparisons in tests instead of checking individual properties

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
src/kernels/execution/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

Notebook-level execution must enqueue cells via CellExecutionQueue and manage per-cell lifecycles through CellExecution

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
src/kernels/**/*.ts

📄 CodeRabbit inference engine (.github/instructions/kernel.instructions.md)

src/kernels/**/*.ts: Use event-driven updates (EventEmitter) for state changes
Monitor and dispose pending promises to prevent leaks during teardown
Use WeakRef/weak references for notebook/object backreferences to avoid memory leaks
Respect CancellationToken in all async operations

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

In unit tests, when a mock is returned from a promise, ensure the mocked instance has an undefined then property to avoid hanging tests

Files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
🧠 Learnings (3)
📚 Learning: 2025-10-14T16:18:46.584Z
Learnt from: Artmann
PR: deepnote/vscode-deepnote#28
File: src/webviews/extension-side/dataframe/dataframeController.ts:19-42
Timestamp: 2025-10-14T16:18:46.584Z
Learning: In the vscode-deepnote codebase, for dataframe renderer commands (SelectPageSizeCommand, GoToPageCommand, CopyTableDataCommand, ExportDataframeCommand), use cellId as the cell identifier and do not add cellIndex as a fallback.

Applied to files:

  • src/webviews/extension-side/dataframe/dataframeController.ts
📚 Learning: 2025-09-03T12:53:28.421Z
Learnt from: CR
PR: deepnote/vscode-extension#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-03T12:53:28.421Z
Learning: Applies to **/*.{ts,tsx} : Use `l10n.t()` for user-facing strings

Applied to files:

  • src/webviews/extension-side/dataframe/dataframeController.ts
📚 Learning: 2025-10-09T11:21:57.494Z
Learnt from: CR
PR: deepnote/vscode-deepnote#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-09T11:21:57.494Z
Learning: Applies to **/*.unit.test.ts : Use assert.deepStrictEqual() for object comparisons in tests instead of checking individual properties

Applied to files:

  • src/kernels/execution/cellExecutionCreator.unit.test.ts
🧬 Code graph analysis (2)
src/webviews/extension-side/dataframe/dataframeController.ts (2)
src/platform/common/types.ts (1)
  • IDisposable (211-213)
src/test/mocks/vsc/extHostedTypes.ts (2)
  • WorkspaceEdit (649-833)
  • NotebookEdit (2444-2507)
src/kernels/execution/cellExecutionCreator.unit.test.ts (2)
src/kernels/types.ts (1)
  • IKernelController (930-933)
src/kernels/execution/cellExecutionCreator.ts (2)
  • NotebookCellExecutionWrapper (22-124)
  • CellExecutionCreator (129-174)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build & Test

Comment on lines +196 to +197
// Now manually call clearOutput to simulate re-execution
// This simulates what CellExecutionCreator.getOrCreate() does when reusing a started wrapper
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify comment about wrapper reuse.

The comment states "This simulates what CellExecutionCreator.getOrCreate() does when reusing a started wrapper," but per the implementation, getOrCreate() always ends the old wrapper and creates a fresh one—it doesn't reuse. The test itself is valid (verifying clearOutput() can be called on a started wrapper), but the comment is misleading.

Update the comment to reflect actual behavior:

-        // Now manually call clearOutput to simulate re-execution
-        // This simulates what CellExecutionCreator.getOrCreate() does when reusing a started wrapper
+        // Now manually call clearOutput to verify it can be invoked on an already-started wrapper
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Now manually call clearOutput to simulate re-execution
// This simulates what CellExecutionCreator.getOrCreate() does when reusing a started wrapper
// Now manually call clearOutput to verify it can be invoked on an already-started wrapper
🤖 Prompt for AI Agents
In src/kernels/execution/cellExecutionCreator.unit.test.ts around lines 196-197,
the existing comment incorrectly states that this simulates
CellExecutionCreator.getOrCreate() reusing a started wrapper; update the comment
to accurately state that getOrCreate() ends the old wrapper and creates a fresh
one, and that this test simply verifies clearOutput() can be called on a started
wrapper (i.e., it manually calls clearOutput to simulate operating on an
already-started wrapper, not to simulate reuse).

_comms: NotebookRendererMessaging,
{ editor, message }: { editor: NotebookEditor; message: DataframeCommand }
) {
logger.info('DataframeController received message', message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Still logging entire message payload.

Line 61 logs the full message, which can include table data (CopyTableDataCommand.data). Past reviews flagged this. Log only command + cellId.

Apply this diff:

-        logger.info('DataframeController received message', message);
+        const safeLog = { command: message.command, cellId: (message as any).cellId };
+        logger.info('DataframeController received message', safeLog);

Based on coding guidelines.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info('DataframeController received message', message);
const safeLog = { command: message.command, cellId: (message as any).cellId };
logger.info('DataframeController received message', safeLog);
🤖 Prompt for AI Agents
In src/webviews/extension-side/dataframe/dataframeController.ts around line 61,
the current logger.info call logs the entire incoming message (including
potentially large/sensitive table payloads). Change the log to only include the
message.command and message.cellId (e.g., logger.info('DataframeController
received message', { command: message.command, cellId: message.cellId })) and
avoid referencing message.data or the full message object; guard against
undefined fields when constructing the log object per coding guidelines.

@Artmann Artmann closed this Oct 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants