Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
a422532
feat: Set up a custom renderer for data frames.
Artmann Oct 8, 2025
3f9c6af
wip
Artmann Oct 9, 2025
e334698
feat: Implement deepnote big number chart support and renderer
tkislan Oct 13, 2025
87f4a09
Merge remote-tracking branch 'origin/chris/display-data-frames' into …
tkislan Oct 13, 2025
62297c2
refactor: Clean up debug logs and improve big number block conversion…
tkislan Oct 13, 2025
7878345
feat: Pass block metadata to cell outputs for renderer to access
tkislan Oct 13, 2025
a12d042
feat: Set up a custom renderer for data frames.
Artmann Oct 8, 2025
c19acd4
wip
Artmann Oct 9, 2025
066229a
add the table state.
Artmann Oct 9, 2025
e36b9c0
page size handling
Artmann Oct 9, 2025
558035f
add page navigation
Artmann Oct 13, 2025
5113db6
Generate Python code before executing the cell.
Artmann Oct 13, 2025
1ba9ab4
clean up
Artmann Oct 13, 2025
a51a150
pr feedback.
Artmann Oct 13, 2025
98d7fe6
Update build/esbuild/build.ts
Artmann Oct 13, 2025
a476bfd
feat: Move DEEPNOTE_VSCODE_RAW_CONTENT_KEY into constants file
tkislan Oct 13, 2025
3a4d579
feedback
Artmann Oct 13, 2025
40ede24
feat: Add chart big number converter tests
tkislan Oct 13, 2025
d220422
Reformat test code
tkislan Oct 14, 2025
78d3cb7
Refactor ChartBigNumberBlockConverter tests to use deepStrictEqual fo…
tkislan Oct 14, 2025
4fa78e2
docs and metadata changes.
Artmann Oct 14, 2025
e8ce411
Merge remote-tracking branch 'origin/chris/display-data-frames' into …
tkislan Oct 14, 2025
8ae05ab
use the latest blocks package.
Artmann Oct 14, 2025
3597ce3
Merge branch 'main' into chris/display-data-frames
Artmann Oct 14, 2025
9fec8d4
add the packages permission
Artmann Oct 14, 2025
0537364
simplify execution flow.
Artmann Oct 14, 2025
9b9216a
remove copyright header
Artmann Oct 14, 2025
e5a1bba
clean up the code
Artmann Oct 14, 2025
e7cccb9
Merge branch 'main' into chris/display-data-frames
Artmann Oct 14, 2025
895fe09
revert controller changes
Artmann Oct 14, 2025
54ca963
pr feedback
Artmann Oct 14, 2025
8309267
Merge branch 'main' into chris/display-data-frames
Artmann Oct 14, 2025
df330bc
pr feedback
Artmann Oct 15, 2025
8b88881
fix the tests
Artmann Oct 15, 2025
8386006
guard metadata spread against undefined.
Artmann Oct 15, 2025
8121bfa
Merge remote-tracking branch 'origin/chris/display-data-frames' into …
tkislan Oct 15, 2025
3bdf030
Merge remote-tracking branch 'origin/main' into tomaskislan/grn-4762-…
tkislan Oct 16, 2025
d9afa1a
fix: Merge cleanup
tkislan Oct 16, 2025
e2b6c36
More merge cleanup
tkislan Oct 16, 2025
722ab50
Fix test
tkislan Oct 16, 2025
628ab6e
feat: Add big number chart json config execution support
tkislan Oct 16, 2025
ed5329d
fix: Enhance error handling for big number metadata parsing and impro…
tkislan Oct 16, 2025
6649ca8
refactor: Simplify chart big number renderer by directly rendering to…
tkislan Oct 16, 2025
a3007ac
Merge remote-tracking branch 'origin/main' into tomaskislan/grn-4762-…
tkislan Oct 16, 2025
b25656b
Fix import
tkislan Oct 16, 2025
8014bc3
fix: Change deepnote_big_number_comparison_type to string type for be…
tkislan Oct 16, 2025
85c8b70
fix: Remove constants, accidentaly added to wrong branch
tkislan Oct 16, 2025
68567e0
Merge branch 'main' into tomaskislan/grn-4762-support-big-number-blocks
tkislan Oct 17, 2025
067f757
Merge remote-tracking branch 'origin/main' into tomaskislan/grn-4762-…
tkislan Oct 17, 2025
cc3f23c
Fix imports
tkislan Oct 17, 2025
6ee3612
fix: Remove unused code
tkislan Oct 17, 2025
95fd687
feat(big-number): Integrate react-error-boundary for error handling a…
tkislan Oct 17, 2025
05ab7a0
Update test snapshots
tkislan Oct 17, 2025
5f05be1
Merge branch 'main' into tomaskislan/grn-4762-support-big-number-blocks
jamesbhobbs Oct 20, 2025
8d83d0c
fix: Fix spell check in test
tkislan Oct 21, 2025
60442ec
Merge remote-tracking branch 'origin/main' into tomaskislan/grn-4762-…
tkislan Oct 21, 2025
236cccd
Merge branch 'main' into tomaskislan/grn-4762-support-big-number-blocks
tkislan Oct 22, 2025
f8dcd85
Merge branch 'main' into tomaskislan/grn-4762-support-big-number-blocks
jamesbhobbs Oct 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions build/esbuild/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,23 @@ async function buildAll() {
),
{ target: 'web', watch: watchAll }
),
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 }
),
build(
path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'chart-big-number-renderer', 'index.ts'),
path.join(
extensionFolder,
'dist',
'webviews',
'webview-side',
'chartBigNumberRenderer',
'chartBigNumberRenderer.js'
),
{ target: 'web', watch: isWatchMode }
),
build(
path.join(extensionFolder, 'src', 'webviews', 'webview-side', 'variable-view', 'index.tsx'),
path.join(extensionFolder, 'dist', 'webviews', 'webview-side', 'viewers', 'variableView.js'),
Expand Down
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 21 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1809,6 +1809,25 @@
"entrypoint": "./dist/webviews/webview-side/ipywidgetsKernel/ipywidgetsKernel.js"
}
],
"notebookRenderer": [
{
"id": "deepnote-dataframe-renderer",
"displayName": "Deepnote Dataframe Renderer",
"entrypoint": "./dist/webviews/webview-side/dataframeRenderer/dataframeRenderer.js",
"mimeTypes": [
"application/vnd.deepnote.dataframe.v3+json"
],
"requiresMessaging": "optional"
},
{
"id": "deepnote-chart-big-number-renderer",
"displayName": "Deepnote Chart Big Number Renderer",
"entrypoint": "./dist/webviews/webview-side/chartBigNumberRenderer/chartBigNumberRenderer.js",
"mimeTypes": [
"application/vnd.deepnote.chart.big-number+json"
]
}
],
"viewsContainers": {
"activitybar": [
{
Expand Down Expand Up @@ -2160,7 +2179,8 @@
"vscode-tas-client": "^0.1.84",
"ws": "^6.2.3",
"zeromq": "^6.5.0",
"zeromqold": "npm:zeromq@^6.0.0-beta.6"
"zeromqold": "npm:zeromq@^6.0.0-beta.6",
"zod": "^4.1.12"
},
"devDependencies": {
"@actions/core": "^1.11.1",
Expand Down
91 changes: 91 additions & 0 deletions src/kernels/deepnote/deepnoteController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import {
CancellationToken,
NotebookCell,
NotebookCellExecution,
NotebookCellOutput,
NotebookCellOutputItem,
NotebookController
} from 'vscode';

import { KernelController } from '../kernelController';

/**
* DeepnoteController extends KernelController to intercept cell execution
* and prepend initialization code to each cell execution.
*/
export class DeepnoteController extends KernelController {
constructor(controller: NotebookController) {
super(controller);
}

public override createNotebookCellExecution(cell: NotebookCell): NotebookCellExecution {
const execution = super.createNotebookCellExecution(cell);
return new DeepnoteNotebookCellExecution(execution, cell);
}
}

/**
* Wrapper around NotebookCellExecution that prepends initialization code.
* This is implemented by delegating all calls to the underlying execution object.
*
* Note: This wrapper currently just delegates to the underlying execution.
* The actual code interception will be implemented at the execution layer.
*/
class DeepnoteNotebookCellExecution implements NotebookCellExecution {
constructor(
private readonly execution: NotebookCellExecution,
public readonly cell: NotebookCell
) {
// Prepend code will be: print("Hello world")
// This will be implemented at the CellExecution layer
}

get executionOrder(): number | undefined {
return this.execution.executionOrder;
}

set executionOrder(value: number | undefined) {
this.execution.executionOrder = value;
}

get token(): CancellationToken {
return this.execution.token;
}

public start(startTime?: number): void {
this.execution.start(startTime);
}

public end(success: boolean | undefined, endTime?: number): void {
this.execution.end(success, endTime);
}

public clearOutput(cell?: NotebookCell): Thenable<void> {
return this.execution.clearOutput(cell);
}

public replaceOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable<void> {
return this.execution.replaceOutput(out, cell);
}

public appendOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable<void> {
return this.execution.appendOutput(out, cell);
}

public replaceOutputItems(
items: NotebookCellOutputItem | readonly NotebookCellOutputItem[],
output: NotebookCellOutput
): Thenable<void> {
return this.execution.replaceOutputItems(items, output);
}

public appendOutputItems(
items: NotebookCellOutputItem | readonly NotebookCellOutputItem[],
output: NotebookCellOutput
): Thenable<void> {
return this.execution.appendOutputItems(items, output);
}
}
4 changes: 4 additions & 0 deletions src/kernels/execution/cellExecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,10 @@ export class CellExecution implements ICellExecution, IDisposable {
return this.completedSuccessfully();
}

// Prepend initialization code
const prependCode = 'print("Hello world")';
code = prependCode + '\n' + code;

// Generate metadata from our cell (some kernels expect this.)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const metadata: any = {
Expand Down
6 changes: 3 additions & 3 deletions src/notebooks/controllers/vscodeNotebookController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import { initializeInteractiveOrNotebookTelemetryBasedOnUserAction } from '../..
import { NotebookCellLanguageService } from '../languages/cellLanguageService';
import { IDataScienceErrorHandler } from '../../kernels/errors/types';
import { ITrustedKernelPaths } from '../../kernels/raw/finder/types';
import { KernelController } from '../../kernels/kernelController';
import { DeepnoteController } from '../../kernels/deepnote/deepnoteController';
import { RemoteKernelReconnectBusyIndicator } from './remoteKernelReconnectBusyIndicator';
import { LastCellExecutionTracker } from '../../kernels/execution/lastCellExecutionTracker';
import type { IAnyMessageArgs } from '@jupyterlab/services/lib/kernel/kernel';
Expand Down Expand Up @@ -548,7 +548,7 @@ export class VSCodeNotebookController implements Disposable, IVSCodeNotebookCont
// Creating these execution objects marks the cell as queued for execution (vscode will update cell UI).
type CellExec = { cell: NotebookCell; exec: NotebookCellExecution };
const cellExecs: CellExec[] = (this.cellQueue.get(doc) || []).map((cell) => {
const exec = this.createCellExecutionIfNecessary(cell, new KernelController(this.controller));
const exec = this.createCellExecutionIfNecessary(cell, new DeepnoteController(this.controller));
return { cell, exec };
});
this.cellQueue.delete(doc);
Expand All @@ -561,7 +561,7 @@ export class VSCodeNotebookController implements Disposable, IVSCodeNotebookCont

// Connect to a matching kernel if possible (but user may pick a different one)
let currentContext: 'start' | 'execution' = 'start';
let controller: IKernelController = new KernelController(this.controller);
let controller: IKernelController = new DeepnoteController(this.controller);
const lastCellExecutionTracker = this.serviceContainer.get<LastCellExecutionTracker>(LastCellExecutionTracker);
let kernel: IKernel | undefined;
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { NotebookCellData, NotebookCellKind } from 'vscode';

import type { BlockConverter } from './blockConverter';
import type { DeepnoteBlock } from '../deepnoteTypes';
import { DeepnoteBigNumberMetadataSchema } from '../deepnoteSchemas';
import { parseJsonWithFallback } from '../dataConversionUtils';
import { z } from 'zod';

export const DEEPNOTE_VSCODE_RAW_CONTENT_KEY = 'deepnote_jupyter_raw_content';
const DEFAULT_BIG_NUMBER_CONFIG = DeepnoteBigNumberMetadataSchema.parse({});

export class ChartBigNumberBlockConverter implements BlockConverter {
applyChangesToBlock(block: DeepnoteBlock, cell: NotebookCellData): void {
block.content = '';

const config = DeepnoteBigNumberMetadataSchema.safeParse(parseJsonWithFallback(cell.value));

if (config.success !== true) {
block.metadata = {
...block.metadata,
[DEEPNOTE_VSCODE_RAW_CONTENT_KEY]: cell.value
};
return;
}

if (block.metadata != null) {
delete block.metadata[DEEPNOTE_VSCODE_RAW_CONTENT_KEY];
}

block.metadata = {
...(block.metadata ?? {}),
...config.data
};
}

canConvert(blockType: string): boolean {
return blockType.toLowerCase() === 'big-number';
}

convertToCell(block: DeepnoteBlock): NotebookCellData {
const deepnoteJupyterRawContentResult = z.string().safeParse(block.metadata?.[DEEPNOTE_VSCODE_RAW_CONTENT_KEY]);
const deepnoteBigNumberMetadataResult = DeepnoteBigNumberMetadataSchema.safeParse(block.metadata);

if (deepnoteBigNumberMetadataResult.error != null) {
console.error('Error parsing deepnote big number metadata:', deepnoteBigNumberMetadataResult.error);
console.debug('Metadata:', JSON.stringify(block.metadata));
}

const configStr = deepnoteJupyterRawContentResult.success
? deepnoteJupyterRawContentResult.data
: deepnoteBigNumberMetadataResult.success
? JSON.stringify(deepnoteBigNumberMetadataResult.data, null, 2)
: JSON.stringify(DEFAULT_BIG_NUMBER_CONFIG);

const cell = new NotebookCellData(NotebookCellKind.Code, configStr, 'json');
console.log(cell);
return cell;
}

getSupportedTypes(): string[] {
return ['big-number'];
}
}
8 changes: 8 additions & 0 deletions src/notebooks/deepnote/dataConversionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
* Utility functions for Deepnote block ID and sorting key generation
*/

export function parseJsonWithFallback(value: string, fallback?: unknown): unknown | null {
try {
return JSON.parse(value);
} catch (error) {
return fallback ?? null;
}
}

/**
* Generate a random hex ID for blocks (32 character hex string)
*/
Expand Down
1 change: 1 addition & 0 deletions src/notebooks/deepnote/deepnoteConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const CHART_BIG_NUMBER_MIME_TYPE = 'application/vnd.deepnote.chart.big-number+json';
28 changes: 24 additions & 4 deletions src/notebooks/deepnote/deepnoteDataConverter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { NotebookCellData, NotebookCellKind, NotebookCellOutput, NotebookCellOutputItem } from 'vscode';

import type { DeepnoteBlock, DeepnoteOutput } from './deepnoteTypes';
import { generateBlockId, generateSortingKey } from './dataConversionUtils';
import type { DeepnoteBlock, DeepnoteOutput } from './deepnoteTypes';
import { ConverterRegistry } from './converters/converterRegistry';
import { CodeBlockConverter } from './converters/codeBlockConverter';
import { addPocketToCellMetadata, createBlockFromPocket } from './pocket';
import { TextBlockConverter } from './converters/textBlockConverter';
import { MarkdownBlockConverter } from './converters/markdownBlockConverter';
import { ChartBigNumberBlockConverter } from './converters/chartBigNumberBlockConverter';
import { CHART_BIG_NUMBER_MIME_TYPE } from './deepnoteConstants';

/**
* Utility class for converting between Deepnote block structures and VS Code notebook cells.
Expand All @@ -19,6 +21,7 @@ export class DeepnoteDataConverter {
this.registry.register(new CodeBlockConverter());
this.registry.register(new TextBlockConverter());
this.registry.register(new MarkdownBlockConverter());
this.registry.register(new ChartBigNumberBlockConverter());
}

/**
Expand Down Expand Up @@ -54,7 +57,7 @@ export class DeepnoteDataConverter {
// The pocket is a place to tuck away Deepnote-specific fields for later.
addPocketToCellMetadata(cell);

cell.outputs = this.transformOutputsForVsCode(block.outputs || []);
cell.outputs = this.transformOutputsForVsCode(block.type, block.outputs || []);

return cell;
});
Expand Down Expand Up @@ -202,7 +205,10 @@ export class DeepnoteDataConverter {
});
}

private transformOutputsForVsCode(outputs: DeepnoteOutput[]): NotebookCellOutput[] {
private transformOutputsForVsCode(
blockType: DeepnoteBlock['type'],
outputs: DeepnoteOutput[]
): NotebookCellOutput[] {
return outputs.map((output) => {
if ('output_type' in output) {
if (output.output_type === 'error') {
Expand Down Expand Up @@ -269,7 +275,21 @@ export class DeepnoteDataConverter {

// Plain text as fallback (always last)
if (data['text/plain']) {
items.push(NotebookCellOutputItem.text(data['text/plain'] as string));
let mimeType = 'text/plain';
if (blockType === 'big-number') {
mimeType = CHART_BIG_NUMBER_MIME_TYPE;
}
items.push(NotebookCellOutputItem.text(data['text/plain'] as string, mimeType));
}

// Deepnote chart big number
if (data[CHART_BIG_NUMBER_MIME_TYPE]) {
items.push(
NotebookCellOutputItem.text(
data[CHART_BIG_NUMBER_MIME_TYPE] as string,
CHART_BIG_NUMBER_MIME_TYPE
)
);
}
}

Expand Down
47 changes: 47 additions & 0 deletions src/notebooks/deepnote/deepnoteSchemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { z } from 'zod';

export const DeepnoteChartBigNumberOutputSchema = z.object({
title: z.string().nullish(),
value: z.string().nullish(),

comparisonTitle: z.string().nullish(),
comparisonValue: z.string().nullish()
});

export const DeepnoteBigNumberMetadataSchema = z.object({
deepnote_big_number_title: z
.string()
.nullish()
.transform((val) => val ?? null),
deepnote_big_number_value: z
.string()
.nullish()
.transform((val) => val ?? null),
deepnote_big_number_format: z
.string()
.nullish()
.transform((val) => val ?? null),
deepnote_big_number_comparison_type: z
.enum(['absolute-value', 'percentage-change', 'absolute-change'])
.nullish()
.transform((val) => val ?? null),
deepnote_big_number_comparison_title: z
.string()
.nullish()
.transform((val) => val ?? null),
deepnote_big_number_comparison_value: z
.string()
.nullish()
.transform((val) => val ?? null),
deepnote_big_number_comparison_format: z
.string()
.nullish()
.transform((val) => val ?? null),
deepnote_big_number_comparison_enabled: z
.boolean()
.nullish()
.transform((val) => val ?? null)
});

export type DeepnoteChartBigNumberOutput = z.infer<typeof DeepnoteChartBigNumberOutputSchema>;
export type DeepnoteBigNumberMetadata = z.infer<typeof DeepnoteBigNumberMetadataSchema>;
Loading