Skip to content

Commit b0ea09b

Browse files
fix: re-add notebook switching inside Deepnote file (#6)
* Rename `deepnoteFileFromServerSchema` for clarity Signed-off-by: Andy Jakubowski <hello@andyjakubowski.com> * Re-add notebook switching Signed-off-by: Andy Jakubowski <hello@andyjakubowski.com> * fix: Check Licenses branch name trigger pattern Signed-off-by: Andy Jakubowski <hello@andyjakubowski.com> * Update readme Signed-off-by: Andy Jakubowski <hello@andyjakubowski.com> --------- Signed-off-by: Andy Jakubowski <hello@andyjakubowski.com>
1 parent 45fb503 commit b0ea09b

File tree

7 files changed

+85
-35
lines changed

7 files changed

+85
-35
lines changed

.github/workflows/check-licenses.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name: Check Licenses
22
on:
33
push:
4-
branches: ["main"]
4+
branches: ['main']
55
pull_request:
6-
branches: ["*"]
6+
branches: ['**']
77

88
concurrency:
99
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -18,7 +18,6 @@ jobs:
1818
uses: actions/checkout@v4
1919
- name: Base Setup
2020
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
21-
21+
2222
- name: Check Licenses
2323
run: yarn check-licenses
24-

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ The `@deepnote/blocks` package is published on GitHub Packages. To install it, y
8282
- Select the `read:packages` scope
8383
- Generate and copy the token
8484

85-
2. Set the `GITHUB_TOKEN` environment variable to ensure `jlpm` (which is a wrapper around Yarn) can download the `@deepnote/blocks` package from the GitHub package registry. You can set the variable in `.zshrc` or manually like:
85+
2. Set the `GITHUB_TOKEN` environment variable to ensure `jlpm` (which is a wrapper around Yarn) can download the `@deepnote/blocks` package from the GitHub package registry. You can export the variable in `.zshrc` (or by reading a `~/.env` file):
8686
```shell
8787
export GITHUB_TOKEN=your_token_here
8888
```

src/components/NotebookPicker.tsx

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import { ReactWidget } from '@jupyterlab/apputils';
33
import { NotebookPanel } from '@jupyterlab/notebook';
44
import { HTMLSelect } from '@jupyterlab/ui-components';
5+
import { deepnoteMetadataSchema } from '../types';
56

67
export class NotebookPicker extends ReactWidget {
78
private selected: string | null = null;
@@ -31,22 +32,30 @@ export class NotebookPicker extends ReactWidget {
3132

3233
const selected = event.target.value;
3334
const deepnoteMetadata = this.panel.context.model.getMetadata('deepnote');
34-
const notebooks = deepnoteMetadata?.notebooks;
35+
const deepnoteMetadataValidated =
36+
deepnoteMetadataSchema.safeParse(deepnoteMetadata);
3537

36-
if (notebooks && selected in notebooks) {
37-
// clone the notebook JSON
38-
const newModelData = { ...notebooks[selected] };
38+
if (!deepnoteMetadataValidated.success) {
39+
console.error(
40+
'Invalid deepnote metadata:',
41+
deepnoteMetadataValidated.error
42+
);
43+
return;
44+
}
3945

40-
// preserve deepnote metadata *without* re-inserting all notebooks
41-
newModelData.metadata = {
42-
...(newModelData.metadata ?? {}),
43-
deepnote: {
44-
notebook_names: deepnoteMetadata?.notebook_names ?? [],
45-
notebooks: deepnoteMetadata?.notebooks ?? {}
46-
}
47-
};
46+
const notebooks = deepnoteMetadataValidated.data.notebooks;
4847

49-
model.fromJSON(newModelData);
48+
if (selected in notebooks) {
49+
model.fromJSON({
50+
cells: notebooks[selected]?.cells ?? [],
51+
metadata: {
52+
deepnote: {
53+
notebooks
54+
}
55+
},
56+
nbformat: 4,
57+
nbformat_minor: 0
58+
});
5059
model.dirty = false;
5160
}
5261

@@ -56,12 +65,13 @@ export class NotebookPicker extends ReactWidget {
5665

5766
render(): JSX.Element {
5867
const deepnoteMetadata = this.panel.context.model.getMetadata('deepnote');
59-
const metadataNames = deepnoteMetadata?.notebook_names;
60-
const names =
61-
Array.isArray(metadataNames) &&
62-
metadataNames.every(n => typeof n === 'string')
63-
? metadataNames
64-
: [];
68+
69+
const deepnoteMetadataValidated =
70+
deepnoteMetadataSchema.safeParse(deepnoteMetadata);
71+
72+
const names = deepnoteMetadataValidated.success
73+
? Object.values(deepnoteMetadataValidated.data.notebooks).map(n => n.name)
74+
: [];
6575

6676
return (
6777
<HTMLSelect

src/deepnote-content-provider.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { transformDeepnoteYamlToNotebookContent } from './transform-deepnote-yam
44

55
export const deepnoteContentProviderName = 'deepnote-content-provider';
66

7-
const deepnoteNotebookSchema = z.object({
7+
const deepnoteFileFromServerSchema = z.object({
88
cells: z.array(z.any()), // or refine further with nbformat
99
metadata: z.object({
1010
deepnote: z.object({
@@ -29,7 +29,7 @@ export class DeepnoteContentProvider extends RestContentProvider {
2929
return model;
3030
}
3131

32-
const validatedModelContent = deepnoteNotebookSchema.safeParse(
32+
const validatedModelContent = deepnoteFileFromServerSchema.safeParse(
3333
model.content
3434
);
3535

@@ -43,6 +43,7 @@ export class DeepnoteContentProvider extends RestContentProvider {
4343
return model;
4444
}
4545

46+
// Transform the Deepnote YAML to Jupyter notebook content
4647
const transformedModelContent =
4748
await transformDeepnoteYamlToNotebookContent(
4849
validatedModelContent.data.metadata.deepnote.rawYamlString

src/fallback-data.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ export const blankDeepnoteNotebookContent: IDeepnoteNotebookContent = {
2121
],
2222
metadata: {
2323
deepnote: {
24-
rawYamlString: null,
25-
deepnoteFile: null
24+
notebooks: {}
2625
}
2726
},
2827
nbformat: 4,

src/transform-deepnote-yaml-to-notebook-content.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IDeepnoteNotebookContent } from './types';
1+
import { IDeepnoteNotebookContent, IDeepnoteNotebookMetadata } from './types';
22
import { blankCodeCell, blankDeepnoteNotebookContent } from './fallback-data';
33
import { deserializeDeepnoteFile } from '@deepnote/blocks';
44
import { convertDeepnoteBlockToJupyterCell } from './convert-deepnote-block-to-jupyter-cell';
@@ -9,6 +9,18 @@ export async function transformDeepnoteYamlToNotebookContent(
99
try {
1010
const deepnoteFile = await deserializeDeepnoteFile(yamlString);
1111

12+
const notebooks = deepnoteFile.project.notebooks.reduce(
13+
(acc, notebook) => {
14+
acc[notebook.name] = {
15+
id: notebook.id,
16+
name: notebook.name,
17+
cells: notebook.blocks.map(convertDeepnoteBlockToJupyterCell)
18+
};
19+
return acc;
20+
},
21+
{} as IDeepnoteNotebookMetadata['deepnote']['notebooks']
22+
);
23+
1224
const selectedNotebook = deepnoteFile.project.notebooks[0];
1325

1426
if (!selectedNotebook) {
@@ -28,8 +40,14 @@ export async function transformDeepnoteYamlToNotebookContent(
2840
);
2941

3042
return {
31-
...blankDeepnoteNotebookContent,
32-
cells
43+
cells,
44+
metadata: {
45+
deepnote: {
46+
notebooks
47+
}
48+
},
49+
nbformat: 4,
50+
nbformat_minor: 0
3351
};
3452
} catch (error) {
3553
console.error('Failed to deserialize Deepnote file:', error);

src/types.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
1-
import { INotebookContent, INotebookMetadata } from '@jupyterlab/nbformat';
2-
import type { DeepnoteFile } from '@deepnote/blocks';
1+
import {
2+
ICodeCell,
3+
IMarkdownCell,
4+
INotebookContent,
5+
INotebookMetadata
6+
} from '@jupyterlab/nbformat';
7+
8+
import { z } from 'zod';
9+
10+
export const deepnoteMetadataSchema = z.object({
11+
notebooks: z.record(
12+
z.string(),
13+
z.object({
14+
id: z.string(),
15+
name: z.string(),
16+
cells: z.array(z.any())
17+
})
18+
)
19+
});
320

421
export interface IDeepnoteNotebookMetadata extends INotebookMetadata {
522
deepnote: {
6-
rawYamlString: string | null;
7-
deepnoteFile: DeepnoteFile | null;
23+
notebooks: Record<
24+
string,
25+
{
26+
id: string;
27+
name: string;
28+
cells: Array<ICodeCell | IMarkdownCell>;
29+
}
30+
>;
831
};
932
}
1033

0 commit comments

Comments
 (0)