Skip to content

Commit a02d45d

Browse files
authored
Export as ipynb (#620)
Heavily cleaned up from initial vibe-coded code - Filters out AI cells - Validates generated JSON with ajv against a JSON schema (Jupyter Notebook v4.5 JSON schema) - Assumes Python 3.10.0 - Hidden behind a feature flag - We can take it out of ff status once we can get the Python version, rather than default of 3.10.0
1 parent c4c3ad7 commit a02d45d

File tree

12 files changed

+1502
-3
lines changed

12 files changed

+1502
-3
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
"@types/react-syntax-highlighter": "^15.5.13",
9898
"@uiw/codemirror-theme-github": "^4.23.14",
9999
"@uiw/react-codemirror": "^4.23.14",
100+
"ajv": "^8.17.1",
100101
"ansi-to-react": "^6.1.6",
101102
"chroma-js": "^3.1.2",
102103
"class-variance-authority": "^0.7.1",

pnpm-lock.yaml

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

src/components/notebooks/notebook/NotebookControls.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import {
88
DropdownMenuSeparator,
99
DropdownMenuTrigger,
1010
} from "@/components/ui/dropdown-menu";
11+
import { useFeatureFlag } from "@/contexts/FeatureFlagContext";
1112
import { useDuplicateNotebook } from "@/hooks/useDuplicateNotebook";
13+
import { useNotebookExport } from "@/hooks/useNotebookExport";
1214
import { useMutation } from "@tanstack/react-query";
13-
import { CopyPlus, MoreHorizontal, Plus, Trash2 } from "lucide-react";
15+
import { CopyPlus, FileDown, MoreHorizontal, Plus, Trash2 } from "lucide-react";
1416
import { useNavigate } from "react-router-dom";
1517
import { toast } from "sonner";
1618
import { useCreateNotebookAndNavigate } from "../dashboard/helpers";
@@ -19,7 +21,6 @@ import type { NotebookProcessed } from "../types";
1921
import { useAuthenticatedUser } from "@/auth/index.js";
2022
import { useDebug } from "@/components/debug/debug-mode";
2123
import { Spinner } from "@/components/ui/Spinner";
22-
import { useFeatureFlag } from "@/contexts/FeatureFlagContext";
2324
import { useRuntimeHealth } from "@/hooks/useRuntimeHealth";
2425
import { runnableCellsWithIndices$, runningCells$ } from "@/queries";
2526
import { generateQueueId } from "@/util/queue-id";
@@ -33,6 +34,8 @@ export function NotebookControls({
3334
}: {
3435
notebook: NotebookProcessed;
3536
}) {
37+
const exportEnabled = useFeatureFlag("ipynb-export");
38+
3639
const allowBulkNotebookControls = useFeatureFlag("bulk-notebook-controls");
3740
const { store } = useStore();
3841
const userId = useAuthenticatedUser();
@@ -85,6 +88,7 @@ export function NotebookControls({
8588
)}
8689
<CreateNotebookAction />
8790
<DuplicateAction notebook={notebook} />
91+
{exportEnabled && <ExportAction />}
8892
<DropdownMenuSeparator />
8993
{debug.enabled && <DeleteAllCellsAction />}
9094
<DeleteAction notebook={notebook} />
@@ -210,6 +214,17 @@ function DuplicateAction({ notebook }: { notebook: NotebookProcessed }) {
210214
);
211215
}
212216

217+
function ExportAction() {
218+
const { exportToJupyter } = useNotebookExport();
219+
220+
return (
221+
<DropdownMenuItem onSelect={exportToJupyter}>
222+
<FileDown />
223+
Download as .ipynb
224+
</DropdownMenuItem>
225+
);
226+
}
227+
213228
function DeleteAction({ notebook }: { notebook: NotebookProcessed }) {
214229
const trpc = useTrpc();
215230
const { confirm } = useConfirm();

src/components/notebooks/notebook/NotebookHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import {
1212
import { Button } from "../../ui/button.js";
1313
import { SimpleUserProfile } from "../SimpleUserProfile.js";
1414
import type { NotebookProcessed } from "../types.js";
15+
import { NotebookControls } from "./NotebookControls.js";
1516
import { TitleEditor } from "./TitleEditor.js";
1617
import { useTitle } from "react-use";
17-
import { NotebookControls } from "./NotebookControls.js";
1818

1919
export function NotebookHeader({
2020
notebook,

src/contexts/FeatureFlagContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useSessionStorage } from "react-use";
44
// Define available feature flags with strict TypeScript definitions
55
export interface FeatureFlags {
66
"test-flag": boolean;
7+
/** Can remove this feature flag once we have a proper way to get the Python version from the kernel */
78
"ipynb-export": boolean;
89
"file-upload": boolean;
910
/** Whether to enable the notebook controls */

src/data/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
nbformat.v4.schema.json is from:
2+
https://github.com/jupyter/nbformat/blob/ceaf199c008c06c5bb45b7c337e520c4f154d67f/nbformat/v4/nbformat.v4.schema.json

0 commit comments

Comments
 (0)