Skip to content

Commit 193b577

Browse files
authored
feat: Jupyter React components (#4659)
* feat: Jupyter React components * Add require.js to root layout * Add Jupyter React components to Plasmic registration * Add necessary webpack config to next.config.js * Add reference in next.config with source for Jupyter config
1 parent c547a85 commit 193b577

File tree

10 files changed

+7529
-352
lines changed

10 files changed

+7529
-352
lines changed

apps/frontend/app/layout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Script from "next/script";
12
import { ApolloWrapper } from "@/components/dataprovider/apollo-wrapper";
23
import { PostHogProvider } from "@/components/dataprovider/posthog-provider";
34
import { SupabaseProvider } from "@/components/hooks/supabase";
@@ -24,6 +25,8 @@ export default function RootLayout({
2425
</SupabaseProvider>
2526
</body>
2627
<GoogleAnalytics />
28+
{/** Require.js is necessary for Jupyter */}
29+
<Script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.7/require.min.js" />
2730
</html>
2831
);
2932
}

apps/frontend/components/widgets/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import {
1616
FeedbackWrapper,
1717
FeedbackWrapperMeta,
1818
} from "@/components/widgets/feedback-farm";
19+
import {
20+
JupyterRootMeta,
21+
JupyterNotebookMeta,
22+
JupyterConsoleMeta,
23+
} from "@/components/widgets/jupyter-meta";
1924
import { Markdown, MarkdownMeta } from "@/components/widgets/markdown";
2025
import {
2126
MonacoEditor,
@@ -41,11 +46,26 @@ export function registerAllWidgets(PLASMIC: NextJsPlasmicComponentLoader) {
4146
dynamic(() => import("./algolia"), { ssr: false }),
4247
AlgoliaSearchListMeta,
4348
);
49+
PLASMIC.registerComponent(
50+
dynamic(() => import("./jupyter-root"), { ssr: false }),
51+
JupyterRootMeta,
52+
);
53+
PLASMIC.registerComponent(
54+
dynamic(() => import("./jupyter-notebook"), { ssr: false }),
55+
JupyterNotebookMeta,
56+
);
57+
PLASMIC.registerComponent(
58+
dynamic(() => import("./jupyter-console"), { ssr: false }),
59+
JupyterConsoleMeta,
60+
);
4461

4562
PLASMIC.registerComponent(AuthActions, AuthActionsMeta);
4663
PLASMIC.registerComponent(AuthForm, AuthFormMeta);
4764
PLASMIC.registerComponent(DynamicConnectorForm, DynamicConnectorFormMeta);
4865
PLASMIC.registerComponent(FeedbackWrapper, FeedbackWrapperMeta);
66+
//PLASMIC.registerComponent(JupyterRoot, JupyterRootMeta);
67+
//PLASMIC.registerComponent(JupyterNotebook, JupyterNotebookMeta);
68+
//PLASMIC.registerComponent(JupyterConsole, JupyterConsoleMeta);
4969
PLASMIC.registerComponent(Markdown, MarkdownMeta);
5070
PLASMIC.registerComponent(MonacoEditor, MonacoEditorMeta);
5171
PLASMIC.registerComponent(Navbar, NavbarMeta);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"use client";
2+
import { Console } from "@datalayer/jupyter-react";
3+
import type { JupyterConsoleProps } from "@/components/widgets/jupyter-meta";
4+
5+
function JupyterConsole(props: JupyterConsoleProps) {
6+
return (
7+
<div className={props.className}>
8+
<Console />
9+
</div>
10+
);
11+
}
12+
13+
export default JupyterConsole;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"use client";
2+
3+
import { CodeComponentMeta } from "@plasmicapp/loader-nextjs";
4+
import type { JupyterProps, INotebookProps } from "@datalayer/jupyter-react";
5+
6+
type JupyterRootProps = Partial<JupyterProps> & {
7+
className?: string;
8+
};
9+
10+
const JupyterRootMeta: CodeComponentMeta<JupyterRootProps> = {
11+
name: "JupyterRoot",
12+
description: "Root wrapper for Jupyter components",
13+
props: {
14+
jupyterServerUrl: "string",
15+
jupyterServerToken: "string",
16+
lite: "boolean",
17+
terminals: "boolean",
18+
},
19+
};
20+
21+
type JupyterNotebookProps = Partial<JupyterProps> &
22+
Partial<INotebookProps> & {
23+
className?: string;
24+
};
25+
26+
const JupyterNotebookMeta: CodeComponentMeta<JupyterNotebookProps> = {
27+
name: "JupyterNotebook",
28+
description: "Fully-featured Jupyter Notebook component",
29+
props: {
30+
ipywidgets: "string",
31+
nbformat: "object",
32+
path: "string",
33+
height: "string",
34+
maxHeight: "string",
35+
readonly: "boolean",
36+
},
37+
};
38+
39+
type JupyterConsoleProps = {
40+
className?: string;
41+
};
42+
43+
const JupyterConsoleMeta: CodeComponentMeta<JupyterConsoleProps> = {
44+
name: "JupyterConsole",
45+
description: "Jupyter Python Console component",
46+
props: {},
47+
};
48+
49+
export type { JupyterRootProps, JupyterNotebookProps, JupyterConsoleProps };
50+
export { JupyterRootMeta, JupyterNotebookMeta, JupyterConsoleMeta };
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"use client";
2+
import { Notebook } from "@datalayer/jupyter-react";
3+
import type { JupyterNotebookProps } from "@/components/widgets/jupyter-meta";
4+
5+
function JupyterNotebook(props: JupyterNotebookProps) {
6+
return <Notebook className={props.className} {...props} />;
7+
}
8+
9+
export default JupyterNotebook;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"use client";
2+
3+
import { Jupyter } from "@datalayer/jupyter-react";
4+
import type { JupyterRootProps } from "@/components/widgets/jupyter-meta";
5+
6+
function JupyterRoot(props: JupyterRootProps) {
7+
return <Jupyter className={props.className} {...props} />;
8+
}
9+
10+
export default JupyterRoot;

apps/frontend/components/widgets/navbar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ type NavBarProps = {
135135
};
136136

137137
const NavbarMeta: CodeComponentMeta<NavBarProps> = {
138-
name: "MarketingNavbar",
139-
description: "NavBar for marketing pages",
138+
name: "Navbar",
139+
description: "shadcn-based NavBar",
140140
props: {
141141
menuItems: {
142142
type: "object",
@@ -149,7 +149,7 @@ const NavbarMeta: CodeComponentMeta<NavBarProps> = {
149149
function Navbar(props: NavBarProps) {
150150
const menuItems: NavBarItem[] = props.menuItems ?? DEFAULT_MENU_ITEMS;
151151
return (
152-
<NavigationMenu>
152+
<NavigationMenu className={props.className}>
153153
<NavigationMenuList>
154154
{menuItems.map((item) =>
155155
item.type === "link" ? (

apps/frontend/next.config.js

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// @ts-check
2+
// eslint-disable-next-line @typescript-eslint/no-require-imports
3+
const webpack = require("webpack");
24

35
/**
46
* @type {import('next').NextConfig}
@@ -115,11 +117,85 @@ const nextConfig = {
115117
},
116118
// This is required to support PostHog trailing slash API requests
117119
skipTrailingSlashRedirect: true,
118-
webpack: (config, { isServer }) => {
119-
if (isServer) {
120+
/**
121+
* JUPYTER SETTINGS
122+
* Mostly copied from https://github.com/datalayer-examples/jupyter-nextjs-example/blob/main/next.config.ts
123+
*/
124+
transpilePackages: ["@jupyterlab/settingregistry", "@jupyterlite/settings"],
125+
webpack: (config, options) => {
126+
if (options.isServer) {
120127
config.plugins = [...config.plugins];
121128
}
122-
129+
config.resolve.fallback = {
130+
...config.resolve.fallback,
131+
buffer: require.resolve("buffer/"),
132+
};
133+
config.plugins.push(
134+
new webpack.ProvidePlugin({
135+
Buffer: ["buffer", "Buffer"],
136+
}),
137+
);
138+
// Fix json5 import issue for JupyterLab packages
139+
config.resolve.alias = {
140+
...config.resolve.alias,
141+
json5: require.resolve("json5/lib/index.js"),
142+
};
143+
// Add a plugin to strip `~` from import paths
144+
config.plugins.push(
145+
new webpack.NormalModuleReplacementPlugin(/^~(.*)/, (resource) => {
146+
resource.request = resource.request.replace(/^~/, "");
147+
}),
148+
);
149+
config.module.rules.push(
150+
{ test: /\.js.map$/, type: "asset/resource" },
151+
{
152+
// In .css files, svg is loaded as a data URI.
153+
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
154+
issuer: /\.css$/,
155+
use: {
156+
loader: "svg-url-loader",
157+
options: { encoding: "none", limit: 10000 },
158+
},
159+
},
160+
{
161+
// In .ts and .tsx files (both of which compile to .js), svg files
162+
// must be loaded as a raw string instead of data URIs.
163+
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
164+
issuer: /\.js$/,
165+
type: "asset/source",
166+
},
167+
// Ship the JupyterLite service worker.
168+
{
169+
resourceQuery: /text/,
170+
type: "asset/resource",
171+
generator: {
172+
filename: "[name][ext]",
173+
},
174+
},
175+
// Rule for pyodide kernel
176+
{
177+
test: /pypi\/.*/,
178+
type: "asset/resource",
179+
generator: {
180+
filename: "pypi/[name][ext][query]",
181+
},
182+
},
183+
// Rule for Python wheel files
184+
{
185+
test: /\.whl$/,
186+
type: "asset/resource",
187+
generator: {
188+
filename: "pypi/[name][ext][query]",
189+
},
190+
},
191+
{
192+
test: /pyodide-kernel-extension\/schema\/.*/,
193+
type: "asset/resource",
194+
generator: {
195+
filename: "schema/[name][ext][query]",
196+
},
197+
},
198+
);
123199
return config;
124200
},
125201
};

apps/frontend/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
"@apollo/subgraph": "^2.11.2",
3838
"@as-integrations/next": "^3.2.0",
3939
"@clickhouse/client": "^1.11.2",
40+
"@datalayer/icons-react": "^1.0.6",
41+
"@datalayer/jupyter-react": "^1.0.5",
42+
"@datalayer/primer-addons": "^1.0.3",
4043
"@emotion/react": "^11.14.0",
4144
"@emotion/styled": "^11.14.1",
4245
"@feedbackfarm/react": "^4.0.10",
@@ -132,10 +135,12 @@
132135
"postcss": "^8.5.6",
133136
"supabase": "^2.31.4",
134137
"supazod": "^2.0.0",
138+
"svg-url-loader": "^8.0.0",
135139
"tailwindcss": "^3.4.4",
136140
"ts-jest": "^29.4.0",
137141
"ts-node": "^10.9.2",
138142
"tsconfig-paths": "^4.2.0",
139-
"typescript": "^5.8.3"
143+
"typescript": "^5.8.3",
144+
"webpack": "^5.101.0"
140145
}
141146
}

0 commit comments

Comments
 (0)