Skip to content

Commit 673bc3a

Browse files
committed
Merge remote-tracking branch 'origin/main' into hannes/grn-4913-manage-deepnote-kernels
Signed-off-by: Tomas Kislan <tomas@kislan.sk>
2 parents c2899d0 + c9dffe8 commit 673bc3a

25 files changed

+1345
-201
lines changed

.github/workflows/ci.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,31 @@ jobs:
190190
- name: Run spell check
191191
run: npm run spell-check
192192

193+
package-lock-drift-check:
194+
name: Package Lock Drift Check
195+
runs-on: ubuntu-latest
196+
timeout-minutes: 5
197+
steps:
198+
- name: Checkout
199+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
200+
201+
- name: Setup Node.js
202+
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
203+
with:
204+
cache: 'npm'
205+
node-version-file: '.nvmrc'
206+
registry-url: 'https://npm.pkg.github.com'
207+
scope: '@deepnote'
208+
209+
- name: Install dependencies
210+
run: npm install
211+
env:
212+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
213+
214+
- name: Check package lock drift
215+
run: |
216+
git diff --exit-code HEAD
217+
193218
audit-prod:
194219
name: Audit - Production
195220
runs-on: ubuntu-latest

package-lock.json

Lines changed: 61 additions & 101 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@
152152
"category": "Deepnote",
153153
"icon": "$(plug)"
154154
},
155+
{
156+
"command": "deepnote.openInDeepnote",
157+
"title": "Open in Deepnote",
158+
"category": "Deepnote",
159+
"icon": "$(globe)"
160+
},
155161
{
156162
"command": "deepnote.newProject",
157163
"title": "%deepnote.commands.newProject.title%",
@@ -182,6 +188,12 @@
182188
"category": "Deepnote",
183189
"icon": "$(graph)"
184190
},
191+
{
192+
"command": "deepnote.addChartBlock",
193+
"title": "%deepnote.commands.addChartBlock.title%",
194+
"category": "Deepnote",
195+
"icon": "$(graph-line)"
196+
},
185197
{
186198
"command": "deepnote.addInputTextBlock",
187199
"title": "%deepnote.commands.addInputTextBlock.title%",
@@ -824,6 +836,11 @@
824836
"when": "editorFocus && editorLangId == python && jupyter.hascodecells && !notebookEditorFocused && isWorkspaceTrusted",
825837
"command": "jupyter.exportfileasnotebook",
826838
"group": "Jupyter3@2"
839+
},
840+
{
841+
"when": "resourceExtname == .deepnote",
842+
"command": "deepnote.openInDeepnote",
843+
"group": "navigation"
827844
}
828845
],
829846
"editor.interactiveWindow.context": [
@@ -918,6 +935,51 @@
918935
"group": "navigation@1",
919936
"when": "notebookType == 'deepnote'"
920937
},
938+
{
939+
"command": "deepnote.addChartBlock",
940+
"group": "navigation@2",
941+
"when": "notebookType == 'deepnote'"
942+
},
943+
{
944+
"command": "deepnote.addBigNumberChartBlock",
945+
"group": "navigation@3",
946+
"when": "notebookType == 'deepnote'"
947+
},
948+
{
949+
"command": "deepnote.addInputTextBlock",
950+
"group": "navigation@4",
951+
"when": "notebookType == 'deepnote'"
952+
},
953+
{
954+
"command": "deepnote.addInputTextareaBlock",
955+
"group": "navigation@5",
956+
"when": "notebookType == 'deepnote'"
957+
},
958+
{
959+
"command": "deepnote.addInputSelectBlock",
960+
"group": "navigation@6",
961+
"when": "notebookType == 'deepnote'"
962+
},
963+
{
964+
"command": "deepnote.addInputSliderBlock",
965+
"group": "navigation@7",
966+
"when": "notebookType == 'deepnote'"
967+
},
968+
{
969+
"command": "deepnote.addInputCheckboxBlock",
970+
"group": "navigation@8",
971+
"when": "notebookType == 'deepnote'"
972+
},
973+
{
974+
"command": "deepnote.addInputDateBlock",
975+
"group": "navigation@9",
976+
"when": "notebookType == 'deepnote'"
977+
},
978+
{
979+
"command": "deepnote.addInputDateRangeBlock",
980+
"group": "navigation@10",
981+
"when": "notebookType == 'deepnote'"
982+
},
921983
{
922984
"command": "jupyter.restartkernel",
923985
"group": "navigation/execute@5",
@@ -1484,6 +1546,18 @@
14841546
"type": "object",
14851547
"title": "Deepnote",
14861548
"properties": {
1549+
"deepnote.domain": {
1550+
"type": "string",
1551+
"default": "deepnote.com",
1552+
"description": "Deepnote domain (e.g., 'deepnote.com' or 'ra-18838.deepnote-staging.com')",
1553+
"scope": "application"
1554+
},
1555+
"deepnote.disableSSLVerification": {
1556+
"type": "boolean",
1557+
"default": false,
1558+
"description": "Disable SSL certificate verification (for development only)",
1559+
"scope": "application"
1560+
},
14871561
"jupyter.experiments.enabled": {
14881562
"type": "boolean",
14891563
"default": true,

package.nls.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@
255255
"deepnote.commands.importNotebook.title": "Import Notebook",
256256
"deepnote.commands.importJupyterNotebook.title": "Import Jupyter Notebook",
257257
"deepnote.commands.addSqlBlock.title": "Add SQL Block",
258-
"deepnote.commands.addBigNumberChartBlock.title": "Add Big Number Chart Block",
258+
"deepnote.commands.addBigNumberChartBlock.title": "Add Big Number Block",
259+
"deepnote.commands.addChartBlock.title": "Add Chart Block",
259260
"deepnote.commands.addInputTextBlock.title": "Add Input Text Block",
260261
"deepnote.commands.addInputTextareaBlock.title": "Add Input Textarea Block",
261262
"deepnote.commands.addInputSelectBlock.title": "Add Input Select Block",

src/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ export interface ICommandNameArgumentTypeMapping {
188188
[DSCommands.ContinueEditSessionInCodespace]: [];
189189
[DSCommands.AddSqlBlock]: [];
190190
[DSCommands.AddBigNumberChartBlock]: [];
191+
[DSCommands.AddChartBlock]: [];
191192
[DSCommands.AddInputTextBlock]: [];
192193
[DSCommands.AddInputTextareaBlock]: [];
193194
[DSCommands.AddInputSelectBlock]: [];
@@ -197,4 +198,5 @@ export interface ICommandNameArgumentTypeMapping {
197198
[DSCommands.AddInputDateRangeBlock]: [];
198199
[DSCommands.AddInputFileBlock]: [];
199200
[DSCommands.AddButtonBlock]: [];
201+
[DSCommands.OpenInDeepnote]: [];
200202
}

src/notebooks/deepnote/blocks.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ project:
4545
type: 'code'
4646
content: "df = pd.DataFrame({'a': [1, 2, 3]})\ndf"
4747
sortingKey: '001'
48-
blockGroup: 'default-group'
48+
blockGroup: 'uuid-v4'
4949
executionCount: 1
5050
metadata:
5151
table_state_spec: '{"pageSize": 25, "pageIndex": 0}'
@@ -150,7 +150,7 @@ Example of a cell after pocket conversion:
150150
__deepnotePocket: {
151151
type: 'code',
152152
sortingKey: '001',
153-
blockGroup: 'default-group',
153+
blockGroup: 'uuid-v4',
154154
executionCount: 1
155155
}
156156
},
@@ -472,7 +472,7 @@ blocks:
472472
type: 'big-number'
473473
content: ''
474474
sortingKey: '001'
475-
blockGroup: 'default-group'
475+
blockGroup: 'uuid-v4'
476476
metadata:
477477
deepnote_big_number_title: 'Customers'
478478
deepnote_big_number_value: 'customers'
@@ -517,7 +517,7 @@ When opened in VS Code, the block becomes a cell with JSON content showing the c
517517
__deepnotePocket: {
518518
type: 'big-number',
519519
sortingKey: '001',
520-
blockGroup: 'default-group'
520+
blockGroup: 'uuid-v4'
521521
}
522522
}
523523
}

src/notebooks/deepnote/deepnoteDataConverter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
ButtonBlockConverter
2828
} from './converters/inputConverters';
2929
import { CHART_BIG_NUMBER_MIME_TYPE } from '../../platform/deepnote/deepnoteConstants';
30+
import { generateUuid } from '../../platform/common/uuid';
3031

3132
/**
3233
* Utility class for converting between Deepnote block structures and VS Code notebook cells.
@@ -168,7 +169,7 @@ export class DeepnoteDataConverter {
168169

169170
private createFallbackBlock(cell: NotebookCellData, index: number): DeepnoteBlock {
170171
return {
171-
blockGroup: 'default-group',
172+
blockGroup: generateUuid(),
172173
id: generateBlockId(),
173174
sortingKey: generateSortingKey(index),
174175
type: cell.kind === NotebookCellKind.Code ? 'code' : 'markdown',

src/notebooks/deepnote/deepnoteExplorerView.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { IDeepnoteNotebookManager } from '../types';
88
import { DeepnoteTreeDataProvider } from './deepnoteTreeDataProvider';
99
import { type DeepnoteTreeItem, DeepnoteTreeItemType, type DeepnoteTreeItemContext } from './deepnoteTreeItem';
1010
import { generateUuid } from '../../platform/common/uuid';
11+
import { DeepnoteBlock, DeepnoteFile } from '@deepnote/blocks';
1112

1213
/**
1314
* Manages the Deepnote explorer tree view and related commands
@@ -213,10 +214,10 @@ export class DeepnoteExplorerView {
213214
const projectId = generateUuid();
214215
const notebookId = generateUuid();
215216

216-
const firstBlock = {
217+
const firstBlock: DeepnoteBlock = {
217218
blockGroup: generateUuid(),
218219
content: '',
219-
executionCount: null,
220+
executionCount: 0,
220221
id: generateUuid(),
221222
metadata: {},
222223
outputs: [],
@@ -225,9 +226,10 @@ export class DeepnoteExplorerView {
225226
version: 1
226227
};
227228

228-
const projectData = {
229-
version: 1.0,
229+
const projectData: DeepnoteFile = {
230+
version: '1.0.0',
230231
metadata: {
232+
createdAt: new Date().toISOString(),
231233
modifiedAt: new Date().toISOString()
232234
},
233235
project: {

src/notebooks/deepnote/deepnoteExplorerView.unit.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ suite('DeepnoteExplorerView - Empty State Commands', () => {
265265
const yamlContent = Buffer.from(capturedContent!).toString('utf8');
266266
const projectData = yaml.load(yamlContent) as any;
267267

268-
expect(projectData.version).to.equal(1.0);
268+
expect(projectData.version).to.equal('1.0.0');
269+
expect(projectData.metadata.createdAt).to.exist;
270+
expect(projectData.metadata.modifiedAt).to.exist;
269271
expect(projectData.project.id).to.equal(projectId);
270272
expect(projectData.project.name).to.equal(projectName);
271273
expect(projectData.project.notebooks).to.have.lengthOf(1);

src/notebooks/deepnote/deepnoteNotebookCommandListener.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { IExtensionSyncActivationService } from '../../platform/activation/types
1717
import { IDisposableRegistry } from '../../platform/common/types';
1818
import { Commands } from '../../platform/common/constants';
1919
import { chainWithPendingUpdates } from '../../kernels/execution/notebookUpdater';
20+
import { WrappedError } from '../../platform/errors/types';
2021
import {
2122
DeepnoteBigNumberMetadataSchema,
2223
DeepnoteTextInputMetadataSchema,
@@ -30,6 +31,7 @@ import {
3031
DeepnoteButtonMetadataSchema,
3132
DeepnoteSqlMetadata
3233
} from './deepnoteSchemas';
34+
import { DATAFRAME_SQL_INTEGRATION_ID } from '../../platform/notebooks/deepnote/integrationTypes';
3335

3436
export type InputBlockType =
3537
| 'input-text'
@@ -75,7 +77,10 @@ export function getInputBlockMetadata(blockType: InputBlockType, variableName: s
7577

7678
export function safeParseDeepnoteVariableNameFromContentJson(content: string): string | undefined {
7779
try {
78-
const variableNameResult = z.string().safeParse(JSON.parse(content)['deepnote_variable_name']);
80+
const parsed = JSON.parse(content);
81+
// Chart blocks use 'variable' key, other blocks use 'deepnote_variable_name'
82+
const variableName = parsed['variable'] ?? parsed['deepnote_variable_name'];
83+
const variableNameResult = z.string().safeParse(variableName);
7984
return variableNameResult.success ? variableNameResult.data : undefined;
8085
} catch (error) {
8186
logger.error('Error parsing deepnote variable name from content JSON', error);
@@ -147,6 +152,7 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation
147152
this.disposableRegistry.push(
148153
commands.registerCommand(Commands.AddBigNumberChartBlock, () => this.addBigNumberChartBlock())
149154
);
155+
this.disposableRegistry.push(commands.registerCommand(Commands.AddChartBlock, () => this.addChartBlock()));
150156
this.disposableRegistry.push(
151157
commands.registerCommand(Commands.AddInputTextBlock, () => this.addInputBlock('input-text'))
152158
);
@@ -189,7 +195,7 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation
189195
const defaultMetadata: DeepnoteSqlMetadata = {
190196
deepnote_variable_name: deepnoteVariableName,
191197
deepnote_return_variable_type: 'dataframe',
192-
sql_integration_id: 'deepnote-dataframe-sql'
198+
sql_integration_id: DATAFRAME_SQL_INTEGRATION_ID
193199
};
194200

195201
// Determine the index where to insert the new cell (below current selection or at the end)
@@ -261,6 +267,61 @@ export class DeepnoteNotebookCommandListener implements IExtensionSyncActivation
261267
await commands.executeCommand('notebook.cell.edit');
262268
}
263269

270+
public async addChartBlock(): Promise<void> {
271+
const editor = window.activeNotebookEditor;
272+
273+
if (!editor) {
274+
throw new WrappedError(l10n.t('No active notebook editor found'));
275+
}
276+
277+
const document = editor.notebook;
278+
const selection = editor.selection;
279+
const insertIndex = selection ? selection.end : document.cellCount;
280+
281+
const defaultVisualizationSpec = {
282+
mark: 'line',
283+
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
284+
data: { values: [] },
285+
encoding: {
286+
x: { field: 'x', type: 'quantitative' },
287+
y: { field: 'y', type: 'quantitative' }
288+
}
289+
};
290+
291+
const cellContent = {
292+
variable: 'df_1',
293+
spec: defaultVisualizationSpec,
294+
filters: []
295+
};
296+
297+
const metadata = {
298+
__deepnotePocket: {
299+
type: 'visualization'
300+
}
301+
};
302+
303+
const result = await chainWithPendingUpdates(document, (edit) => {
304+
const newCell = new NotebookCellData(NotebookCellKind.Code, JSON.stringify(cellContent, null, 2), 'json');
305+
306+
newCell.metadata = metadata;
307+
308+
const nbEdit = NotebookEdit.insertCells(insertIndex, [newCell]);
309+
310+
edit.set(document.uri, [nbEdit]);
311+
});
312+
313+
if (result !== true) {
314+
throw new WrappedError(l10n.t('Failed to insert chart block'));
315+
}
316+
317+
const notebookRange = new NotebookRange(insertIndex, insertIndex + 1);
318+
319+
editor.revealRange(notebookRange, NotebookEditorRevealType.Default);
320+
editor.selection = notebookRange;
321+
322+
await commands.executeCommand('notebook.cell.edit');
323+
}
324+
264325
public async addInputBlock(blockType: InputBlockType): Promise<void> {
265326
const editor = window.activeNotebookEditor;
266327
if (!editor) {

0 commit comments

Comments
 (0)