Skip to content

Commit ca53a42

Browse files
Switch notebooks
Signed-off-by: Andy Jakubowski <hello@andyjakubowski.com>
1 parent 1a2485a commit ca53a42

File tree

2 files changed

+79
-15
lines changed

2 files changed

+79
-15
lines changed

jupyterlab_deepnote/contents.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,40 @@ def yaml_to_ipynb(yaml_text: str):
2020
if not notebooks:
2121
return new_notebook(cells=[])
2222

23+
# Collect notebook names
24+
notebook_names = [nb.get("name", "") for nb in notebooks]
25+
26+
# Build all_notebooks dict: name -> full nbformat notebook JSON
27+
all_notebooks = {}
28+
for nb in notebooks:
29+
nb_blocks = nb.get("blocks", [])
30+
nb_cells = []
31+
for block in sorted(nb_blocks, key=lambda b: b.get("sortingKey", "")):
32+
btype = block.get("type", "code")
33+
content = block.get("content", "")
34+
if btype == "code":
35+
nb_cells.append(new_code_cell(content))
36+
else:
37+
nb_cells.append(new_markdown_cell(content))
38+
# Use the notebook name as key
39+
nb_name = nb.get("name", "")
40+
all_notebooks[nb_name] = new_notebook(cells=nb_cells)
41+
42+
# Use first notebook's cells to render initially
2343
nb0 = notebooks[0]
2444
blocks = nb0.get("blocks", [])
2545
cells = []
26-
2746
for block in sorted(blocks, key=lambda b: b.get("sortingKey", "")):
2847
btype = block.get("type", "code")
2948
content = block.get("content", "")
30-
3149
if btype == "code":
3250
cells.append(new_code_cell(content))
3351
else:
3452
cells.append(new_markdown_cell(content))
3553

36-
notebook_names = [nb.get("name", "") for nb in notebooks]
37-
metadata = {"notebook_names": notebook_names}
54+
metadata = {
55+
"deepnote": {"notebook_names": notebook_names, "notebooks": all_notebooks}
56+
}
3857
return new_notebook(cells=cells, metadata=metadata)
3958

4059

src/index.tsx

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,59 @@ const plugin: JupyterFrontEndPlugin<void> = {
5454
};
5555

5656
class NotebookPicker extends ReactWidget {
57+
private selected: string | null = null;
58+
5759
constructor(private panel: NotebookPanel) {
5860
super();
59-
// when the context becomes ready, trigger re-render
61+
6062
void panel.context.ready.then(() => {
63+
const deepnoteMetadata = this.panel.context.model.getMetadata('deepnote');
64+
const metadataNames = deepnoteMetadata?.notebook_names;
65+
const names =
66+
Array.isArray(metadataNames) &&
67+
metadataNames.every(n => typeof n === 'string')
68+
? metadataNames
69+
: [];
70+
71+
this.selected = names.length > 0 ? names[0] : null;
6172
this.update();
6273
});
6374
}
6475

76+
private handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
77+
const model = this.panel.model;
78+
if (!model) {
79+
return;
80+
}
81+
82+
const selected = event.target.value;
83+
const deepnoteMetadata = this.panel.context.model.getMetadata('deepnote');
84+
const notebooks = deepnoteMetadata?.notebooks;
85+
86+
if (notebooks && selected in notebooks) {
87+
// clone the notebook JSON
88+
const newModelData = { ...notebooks[selected] };
89+
90+
// preserve deepnote metadata *without* re-inserting all notebooks
91+
newModelData.metadata = {
92+
...(newModelData.metadata ?? {}),
93+
deepnote: {
94+
notebook_names: deepnoteMetadata?.notebook_names ?? [],
95+
notebooks: deepnoteMetadata?.notebooks ?? {}
96+
}
97+
};
98+
99+
model.fromJSON(newModelData);
100+
model.dirty = false;
101+
}
102+
103+
this.selected = selected;
104+
this.update();
105+
};
106+
65107
render(): JSX.Element {
66-
const metadataNames =
67-
this.panel.context.model.getMetadata('notebook_names');
108+
const deepnoteMetadata = this.panel.context.model.getMetadata('deepnote');
109+
const metadataNames = deepnoteMetadata?.notebook_names;
68110
const names =
69111
Array.isArray(metadataNames) &&
70112
metadataNames.every(n => typeof n === 'string')
@@ -73,18 +115,21 @@ class NotebookPicker extends ReactWidget {
73115

74116
return (
75117
<HTMLSelect
76-
value={names[0] ?? '-'}
77-
onChange={() => {}}
118+
value={this.selected ?? '-'}
119+
onChange={this.handleChange}
78120
onKeyDown={() => {}}
79121
aria-label="Select active notebook"
80122
title="Select active notebook"
81123
>
82-
<option value="-">-</option>
83-
{names.map(n => (
84-
<option key={n} value={n}>
85-
{n}
86-
</option>
87-
))}
124+
{names.length === 0 ? (
125+
<option value="-">-</option>
126+
) : (
127+
names.map(n => (
128+
<option key={n} value={n}>
129+
{n}
130+
</option>
131+
))
132+
)}
88133
</HTMLSelect>
89134
);
90135
}

0 commit comments

Comments
 (0)