Skip to content

Deepnote add-block commands have no enablement and fire on any active notebook (e.g. .ipynb) #467

Description

@tkislan

All 19 deepnote.add*Block* commands are contributed with no enablement clause, and their handlers never check the active notebook's type — so they appear in the Command Palette for every notebook and will insert a Deepnote-shaped cell into an .ipynb or Interactive Window.

This is a pre-existing gap, not something introduced by a specific PR: on origin/main all 18 pre-existing add-block commands already have enablement: (none) and none of them appear in contributes.menus.commandPalette. deepnote.addAgentBlock (added in #358) simply follows the same pattern. Filing as a sweep rather than patching one command.

Affected commands

All entries below are in package.json under contributes.commands with no enablement key. Handlers all live in src/notebooks/deepnote/deepnoteNotebookCommandListener.ts; ids are declared in src/platform/common/constants.ts:230-248.

Command id Palette title package.json Registration Handler
deepnote.addAgentBlock Add Agent Block package.json:177 :170 addAgentBlock() :243
deepnote.addSqlBlock Add SQL Block package.json:183 :171 addSqlBlock() :305
deepnote.addBigNumberChartBlock Add Big Number Block package.json:189 :173 addBigNumberChartBlock() :351
deepnote.addChartBlock Add Chart Block package.json:195 :175 addChartBlock() :394
deepnote.addInputTextBlock Add Input Text Block package.json:201 :177 addInputBlock() :451
deepnote.addInputTextareaBlock Add Input Textarea Block package.json:207 :180 addInputBlock() :451
deepnote.addInputSelectBlock Add Input Select Block package.json:213 :183 addInputBlock() :451
deepnote.addInputSliderBlock Add Input Slider Block package.json:219 :186 addInputBlock() :451
deepnote.addInputCheckboxBlock Add Input Checkbox Block package.json:225 :189 addInputBlock() :451
deepnote.addInputDateBlock Add Input Date Block package.json:231 :192 addInputBlock() :451
deepnote.addInputDateRangeBlock Add Input Date Range Block package.json:237 :195 addInputBlock() :451
deepnote.addInputFileBlock Add Input File Block package.json:243 :198 addInputBlock() :451
deepnote.addButtonBlock Add Button Block package.json:249 :201 addInputBlock() :451
deepnote.addInputBlock Add Input Block package.json:255 :204 addInputBlockThroughPicker() :537
deepnote.addTextBlock Add Text Block package.json:261 :207 addTextBlockThroughPicker() :499
deepnote.addTextBlockParagraph Add Paragraph Block package.json:267 :225 addTextBlockCommandHandler() :582
deepnote.addTextBlockHeading1 Add Heading 1 Block package.json:273 :210 addTextBlockCommandHandler() :582
deepnote.addTextBlockHeading2 Add Heading 2 Block package.json:279 :215 addTextBlockCommandHandler() :582
deepnote.addTextBlockHeading3 Add Heading 3 Block package.json:285 :220 addTextBlockCommandHandler() :582

Supporting facts, all verified on this branch:

  • grep -c notebookType src/notebooks/deepnote/deepnoteNotebookCommandListener.ts0. Every handler starts from window.activeNotebookEditor and inserts unconditionally.
  • No add-block command has a contributes.menus.commandPalette entry, so none is hidden from the palette.
  • Only 6 of the 19 are surfaced in the toolbar (notebook/toolbar, package.json:1041, :1046, :1051, :1056, :1061, :1066) — and those entries already carry "when": "notebookType == 'deepnote'". The other 13 are palette-only, so the palette is currently their only entry point and it is ungated.

Symptom

  1. Open any .ipynb (or an Interactive Window) so it is the active notebook editor.
  2. Command Palette → e.g. Deepnote: Add SQL Block.
  3. A cell is inserted into the Jupyter notebook with Deepnote-only metadata (__deepnotePocket, deepnote_variable_name, …) and a Deepnote-specific language/content — sql for SQL blocks, json holding a Vega-Lite or big-number spec for chart blocks, plaintext for the agent block.

The cell is meaningless to the Jupyter kernel and will fail if run. Nothing crashes, and the extension does not corrupt the file.

Persisted damage is small — this is mostly a UX/discoverability bug. VS Code's built-in .ipynb serializer writes only the nested cell.metadata.metadata (plus id / attachments / execution_count) back to disk; top-level extension keys like __deepnotePocket are dropped on save. The same nesting convention is visible in this repo at src/platform/common/utils.ts:437 (getCellMetadata — "metadata property is never optional"). Consequently, reopening the saved .ipynb yields a plain cell with odd content and no Deepnote metadata: what survives is a stray cell, not a Deepnote block. (Not observed end-to-end in a running VS Code; based on the serializer's documented metadata shape and this repo's own handling of it.)

Suggested fix

Add an enablement clause to each of the 19 command contributions, matching the predicate the toolbar menu entries already use:

{
    "command": "deepnote.addSqlBlock",
    "title": "%deepnote.commands.addSqlBlock.title%",
    "category": "Deepnote",
    "icon": "$(database)",
    "enablement": "notebookType == 'deepnote'"
}

The notebookType == … form is already established in this file — see package.json:369, :376 and :426 (notebookType == jupyter-notebook && isWorkspaceTrusted && …) for enablement, and :1041:1066 for the quoted notebookType == 'deepnote' form used by the toolbar menus. Reuse the quoted form for consistency with the sibling menu entries.

enablement greys the command out and blocks palette invocation, which covers the reported symptom on its own. Optionally add a cheap handler-side guard in deepnoteNotebookCommandListener.ts next to the existing if (!editor) checks, so programmatic commands.executeCommand(...) callers get the same answer as the palette:

if (editor.notebook.notebookType !== DEEPNOTE_NOTEBOOK_TYPE) {
    throw new Error(l10n.t('Deepnote blocks can only be added to Deepnote notebooks'));
}

Both changes are mechanical and easy to unit-test — the existing suite in deepnoteNotebookCommandListener.unit.test.ts already drives these commands.

Out of scope

  • A fix in feat(agent-block): Add support for Agent block #358. The agent block only inherits an existing pattern; gating it alone would leave 18 siblings open.
  • The agent block's execution-routing exposure, which is a genuinely sharper case and deserves its own treatment. __deepnotePocket.type === 'agent' is read by isAgentCell (src/notebooks/deepnote/dataConversionUtils.ts:30) inside VSCodeNotebookController.executeQueuedCells (src/notebooks/controllers/vscodeNotebookController.ts:653) — a class instantiated for jupyter-notebook and interactive as well as deepnote (src/notebooks/controllers/controllerRegistration.ts:149, :362). A cell carrying that metadata is routed to executeAgentCell instead of the kernel, so it changes what "Run cell" does, not just how the cell looks. Palette enablement does not fully close that: the metadata can also arrive in-session by copying a cell from a .deepnote editor and pasting it into an .ipynb (expected from VS Code's cell clipboard carrying metadata; not verified here). Note that metadata loaded from an .ipynb file lands nested under cell.metadata.metadata and therefore would not trigger routing — the exposure is in-session only. Track separately if the routing guard is wanted.
  • Other ungated Deepnote-category commands (deepnote.newNotebook, deepnote.addNotebookToProject, deepnote.renameProject, the deepnote.environments.* family, …). ~46 Deepnote commands lack enablement in total; those are tree/project commands that error harmlessly rather than mutating an unrelated document, so they are a separate cleanup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions