{
+ console.info("submit");
+ // sleep for 2s
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ throw new Error("error, go away now");
+ // return "dummy";
+ }}
+ />
+ >,
+ );
+ return newElement;
+ };
const renderTopRightUI = (isMobile: boolean) => {
return (
<>
@@ -332,8 +444,8 @@ export default function App({ appTitle, useCustom, customArgs }: AppProps) {
pointerDownState: PointerDownState,
) => {
return withBatchedUpdates((event) => {
- window.removeEventListener(EVENT.POINTER_MOVE, pointerDownState.onMove);
- window.removeEventListener(EVENT.POINTER_UP, pointerDownState.onUp);
+ window.removeEventListener("pointermove", pointerDownState.onMove);
+ window.removeEventListener("pointerup", pointerDownState.onUp);
excalidrawAPI?.setActiveTool({ type: "selection" });
const distance = distance2d(
pointerDownState.x,
@@ -397,8 +509,8 @@ export default function App({ appTitle, useCustom, customArgs }: AppProps) {
onPointerMoveFromPointerDownHandler(pointerDownState);
const onPointerUp =
onPointerUpFromPointerDownHandler(pointerDownState);
- window.addEventListener(EVENT.POINTER_MOVE, onPointerMove);
- window.addEventListener(EVENT.POINTER_UP, onPointerUp);
+ window.addEventListener("pointermove", onPointerMove);
+ window.addEventListener("pointerup", onPointerUp);
pointerDownState.onMove = onPointerMove;
pointerDownState.onUp = onPointerUp;
@@ -490,7 +602,7 @@ export default function App({ appTitle, useCustom, customArgs }: AppProps) {
}}
onBlur={saveComment}
onKeyDown={(event) => {
- if (!event.shiftKey && event.key === KEYS.ENTER) {
+ if (!event.shiftKey && event.key === "Enter") {
event.preventDefault();
saveComment();
}
@@ -523,7 +635,12 @@ export default function App({ appTitle, useCustom, customArgs }: AppProps) {
- {excalidrawAPI && }
+ {excalidrawAPI && (
+
+ )}
);
};
@@ -672,83 +789,7 @@ export default function App({ appTitle, useCustom, customArgs }: AppProps) {
-
- setExcalidrawAPI(api)
- }
- initialData={initialStatePromiseRef.current.promise}
- onChange={(elements, state) => {
- // console.info("Elements :", elements, "State : ", state);
- }}
- onPointerUpdate={(payload: {
- pointer: { x: number; y: number };
- button: "down" | "up";
- pointersMap: Gesture["pointers"];
- }) => setPointerData(payload)}
- viewModeEnabled={viewModeEnabled}
- zenModeEnabled={zenModeEnabled}
- gridModeEnabled={gridModeEnabled}
- theme={theme}
- name="Custom name of drawing"
- UIOptions={{
- canvasActions: {
- loadScene: false,
- },
- tools: { image: !disableImageTool },
- }}
- renderTopRightUI={renderTopRightUI}
- onLinkOpen={onLinkOpen}
- onPointerDown={onPointerDown}
- onScrollChange={rerenderCommentIcons}
- // allow all urls
- validateEmbeddable={true}
- >
- {excalidrawAPI && (
-
- )}
-
-
-
-
- Tab one!
- Tab two!
-
- One
- Two
-
-
-
-
- Toggle Custom Sidebar
-
- {renderMenu()}
- {excalidrawAPI && (
- 😀}>
- Text to diagram
-
- )}
- {
- console.info("submit");
- // sleep for 2s
- await new Promise((resolve) => setTimeout(resolve, 2000));
- throw new Error("error, go away now");
- // return "dummy";
- }}
- />
-
+ {renderExcalidraw(children)}
{Object.keys(commentIcons || []).length > 0 && renderCommentIcons()}
{comment && renderComment()}
diff --git a/packages/excalidraw/example/CustomFooter.tsx b/examples/excalidraw/components/CustomFooter.tsx
similarity index 79%
rename from packages/excalidraw/example/CustomFooter.tsx
rename to examples/excalidraw/components/CustomFooter.tsx
index c4ff5b6422..30d51ecf00 100644
--- a/packages/excalidraw/example/CustomFooter.tsx
+++ b/examples/excalidraw/components/CustomFooter.tsx
@@ -1,6 +1,6 @@
-import type { ExcalidrawImperativeAPI } from "../types";
+import type * as TExcalidraw from "@excalidraw/excalidraw";
+import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/dist/excalidraw/types";
-const { Button, MIME_TYPES } = window.ExcalidrawLib;
const COMMENT_SVG = (
);
+
const CustomFooter = ({
excalidrawAPI,
+ excalidrawLib,
}: {
excalidrawAPI: ExcalidrawImperativeAPI;
+ excalidrawLib: typeof TExcalidraw;
}) => {
+ const { Button, MIME_TYPES } = excalidrawLib;
+
return (
<>
-
-
+
>
);
};
diff --git a/examples/excalidraw/components/MobileFooter.tsx b/examples/excalidraw/components/MobileFooter.tsx
new file mode 100644
index 0000000000..7ab62b918d
--- /dev/null
+++ b/examples/excalidraw/components/MobileFooter.tsx
@@ -0,0 +1,27 @@
+import { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/dist/excalidraw/types";
+import CustomFooter from "./CustomFooter";
+import type * as TExcalidraw from "@excalidraw/excalidraw";
+
+const MobileFooter = ({
+ excalidrawAPI,
+ excalidrawLib,
+}: {
+ excalidrawAPI: ExcalidrawImperativeAPI;
+ excalidrawLib: typeof TExcalidraw;
+}) => {
+ const { useDevice, Footer } = excalidrawLib;
+
+ const device = useDevice();
+ if (device.editor.isMobile) {
+ return (
+
+ );
+ }
+ return null;
+};
+export default MobileFooter;
diff --git a/packages/excalidraw/example/sidebar/ExampleSidebar.scss b/examples/excalidraw/components/sidebar/ExampleSidebar.scss
similarity index 100%
rename from packages/excalidraw/example/sidebar/ExampleSidebar.scss
rename to examples/excalidraw/components/sidebar/ExampleSidebar.scss
diff --git a/packages/excalidraw/example/sidebar/ExampleSidebar.tsx b/examples/excalidraw/components/sidebar/ExampleSidebar.tsx
similarity index 90%
rename from packages/excalidraw/example/sidebar/ExampleSidebar.tsx
rename to examples/excalidraw/components/sidebar/ExampleSidebar.tsx
index a6e1b64750..8b475f16fa 100644
--- a/packages/excalidraw/example/sidebar/ExampleSidebar.tsx
+++ b/examples/excalidraw/components/sidebar/ExampleSidebar.tsx
@@ -1,9 +1,8 @@
+import { useState } from "react";
import "./ExampleSidebar.scss";
-const React = window.React;
-
export default function Sidebar({ children }: { children: React.ReactNode }) {
- const [open, setOpen] = React.useState(false);
+ const [open, setOpen] = useState(false);
return (
<>
diff --git a/packages/excalidraw/example/initialData.tsx b/examples/excalidraw/initialData.tsx
similarity index 99%
rename from packages/excalidraw/example/initialData.tsx
rename to examples/excalidraw/initialData.tsx
index 0299e49596..3cb5e7af4d 100644
--- a/packages/excalidraw/example/initialData.tsx
+++ b/examples/excalidraw/initialData.tsx
@@ -1,5 +1,5 @@
-import type { ExcalidrawElementSkeleton } from "../data/transform";
-import type { FileId } from "../element/types";
+import type { ExcalidrawElementSkeleton } from "@excalidraw/excalidraw/data/transform";
+import type { FileId } from "@excalidraw/excalidraw/element/types";
const elements: ExcalidrawElementSkeleton[] = [
{
diff --git a/examples/excalidraw/package.json b/examples/excalidraw/package.json
new file mode 100644
index 0000000000..fe48d55321
--- /dev/null
+++ b/examples/excalidraw/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "examples",
+ "version": "1.0.0",
+ "private": true,
+ "dependencies": {
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "@excalidraw/excalidraw": "*"
+ },
+ "devDependencies": {
+ "typescript": "^5"
+ }
+}
diff --git a/examples/excalidraw/tsconfig.json b/examples/excalidraw/tsconfig.json
new file mode 100644
index 0000000000..41716a7dd5
--- /dev/null
+++ b/examples/excalidraw/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../tsconfig"
+}
diff --git a/examples/excalidraw/utils.ts b/examples/excalidraw/utils.ts
new file mode 100644
index 0000000000..822be29b78
--- /dev/null
+++ b/examples/excalidraw/utils.ts
@@ -0,0 +1,146 @@
+import { unstable_batchedUpdates } from "react-dom";
+import { fileOpen as _fileOpen } from "browser-fs-access";
+import type { MIME_TYPES } from "@excalidraw/excalidraw";
+import { AbortError } from "../../packages/excalidraw/errors";
+
+type FILE_EXTENSION = Exclude;
+
+const INPUT_CHANGE_INTERVAL_MS = 500;
+
+export type ResolvablePromise = Promise & {
+ resolve: [T] extends [undefined] ? (value?: T) => void : (value: T) => void;
+ reject: (error: Error) => void;
+};
+export const resolvablePromise = () => {
+ let resolve!: any;
+ let reject!: any;
+ const promise = new Promise((_resolve, _reject) => {
+ resolve = _resolve;
+ reject = _reject;
+ });
+ (promise as any).resolve = resolve;
+ (promise as any).reject = reject;
+ return promise as ResolvablePromise;
+};
+
+export const distance2d = (x1: number, y1: number, x2: number, y2: number) => {
+ const xd = x2 - x1;
+ const yd = y2 - y1;
+ return Math.hypot(xd, yd);
+};
+
+export const fileOpen = (opts: {
+ extensions?: FILE_EXTENSION[];
+ description: string;
+ multiple?: M;
+}): Promise => {
+ // an unsafe TS hack, alas not much we can do AFAIK
+ type RetType = M extends false | undefined ? File : File[];
+
+ const mimeTypes = opts.extensions?.reduce((mimeTypes, type) => {
+ mimeTypes.push(MIME_TYPES[type]);
+
+ return mimeTypes;
+ }, [] as string[]);
+
+ const extensions = opts.extensions?.reduce((acc, ext) => {
+ if (ext === "jpg") {
+ return acc.concat(".jpg", ".jpeg");
+ }
+ return acc.concat(`.${ext}`);
+ }, [] as string[]);
+
+ return _fileOpen({
+ description: opts.description,
+ extensions,
+ mimeTypes,
+ multiple: opts.multiple ?? false,
+ legacySetup: (resolve, reject, input) => {
+ const scheduleRejection = debounce(reject, INPUT_CHANGE_INTERVAL_MS);
+ const focusHandler = () => {
+ checkForFile();
+ document.addEventListener("keyup", scheduleRejection);
+ document.addEventListener("pointerup", scheduleRejection);
+ scheduleRejection();
+ };
+ const checkForFile = () => {
+ // this hack might not work when expecting multiple files
+ if (input.files?.length) {
+ const ret = opts.multiple ? [...input.files] : input.files[0];
+ resolve(ret as RetType);
+ }
+ };
+ requestAnimationFrame(() => {
+ window.addEventListener("focus", focusHandler);
+ });
+ const interval = window.setInterval(() => {
+ checkForFile();
+ }, INPUT_CHANGE_INTERVAL_MS);
+ return (rejectPromise) => {
+ clearInterval(interval);
+ scheduleRejection.cancel();
+ window.removeEventListener("focus", focusHandler);
+ document.removeEventListener("keyup", scheduleRejection);
+ document.removeEventListener("pointerup", scheduleRejection);
+ if (rejectPromise) {
+ // so that something is shown in console if we need to debug this
+ console.warn("Opening the file was canceled (legacy-fs).");
+ rejectPromise(new AbortError());
+ }
+ };
+ },
+ }) as Promise;
+};
+
+export const debounce = (
+ fn: (...args: T) => void,
+ timeout: number,
+) => {
+ let handle = 0;
+ let lastArgs: T | null = null;
+ const ret = (...args: T) => {
+ lastArgs = args;
+ clearTimeout(handle);
+ handle = window.setTimeout(() => {
+ lastArgs = null;
+ fn(...args);
+ }, timeout);
+ };
+ ret.flush = () => {
+ clearTimeout(handle);
+ if (lastArgs) {
+ const _lastArgs = lastArgs;
+ lastArgs = null;
+ fn(..._lastArgs);
+ }
+ };
+ ret.cancel = () => {
+ lastArgs = null;
+ clearTimeout(handle);
+ };
+ return ret;
+};
+
+export const withBatchedUpdates = <
+ TFunction extends ((event: any) => void) | (() => void),
+>(
+ func: Parameters["length"] extends 0 | 1 ? TFunction : never,
+) =>
+ ((event) => {
+ unstable_batchedUpdates(func as TFunction, event);
+ }) as TFunction;
+
+/**
+ * barches React state updates and throttles the calls to a single call per
+ * animation frame
+ */
+export const withBatchedUpdatesThrottled = <
+ TFunction extends ((event: any) => void) | (() => void),
+>(
+ func: Parameters["length"] extends 0 | 1 ? TFunction : never,
+) => {
+ // @ts-ignore
+ return throttleRAF>(((event) => {
+ unstable_batchedUpdates(func, event);
+ }) as TFunction);
+};
diff --git a/examples/excalidraw/with-nextjs/.gitignore b/examples/excalidraw/with-nextjs/.gitignore
new file mode 100644
index 0000000000..fd3dbb571a
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/.gitignore
@@ -0,0 +1,36 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+.yarn/install-state.gz
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/examples/excalidraw/with-nextjs/README.md b/examples/excalidraw/with-nextjs/README.md
new file mode 100644
index 0000000000..9e8d9b96d3
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3005) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/examples/excalidraw/with-nextjs/next.config.js b/examples/excalidraw/with-nextjs/next.config.js
new file mode 100644
index 0000000000..701438ebfa
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/next.config.js
@@ -0,0 +1,12 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ distDir: "build",
+ typescript: {
+ // The ts config doesn't work with `jsx: preserve" and if updated to `react-jsx` it gets ovewritten by next js throwing ts errors hence I am ignoring build errors until this is fixed.
+ ignoreBuildErrors: true,
+ },
+ // This is needed as in pages router the code for importing types throws error as its outside next js app
+ transpilePackages: ["../"],
+};
+
+module.exports = nextConfig;
diff --git a/examples/excalidraw/with-nextjs/package.json b/examples/excalidraw/with-nextjs/package.json
new file mode 100644
index 0000000000..1779524072
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "with-nextjs",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "build:workspace": "yarn workspace @excalidraw/excalidraw run build:esm",
+ "dev": "yarn build:workspace && next dev -p 3005",
+ "build": "yarn build:workspace && next build",
+ "start": "next start -p 3006",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@excalidraw/excalidraw": "*",
+ "next": "14.1",
+ "react": "^18",
+ "react-dom": "^18"
+ },
+ "devDependencies": {
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "path2d-polyfill": "2.0.1",
+ "typescript": "^5"
+ }
+}
diff --git a/packages/excalidraw/example/public/images/doremon.png b/examples/excalidraw/with-nextjs/public/images/doremon.png
similarity index 100%
rename from packages/excalidraw/example/public/images/doremon.png
rename to examples/excalidraw/with-nextjs/public/images/doremon.png
diff --git a/packages/excalidraw/example/public/images/excalibot.png b/examples/excalidraw/with-nextjs/public/images/excalibot.png
similarity index 100%
rename from packages/excalidraw/example/public/images/excalibot.png
rename to examples/excalidraw/with-nextjs/public/images/excalibot.png
diff --git a/packages/excalidraw/example/public/images/pika.jpeg b/examples/excalidraw/with-nextjs/public/images/pika.jpeg
similarity index 100%
rename from packages/excalidraw/example/public/images/pika.jpeg
rename to examples/excalidraw/with-nextjs/public/images/pika.jpeg
diff --git a/packages/excalidraw/example/public/images/rocket.jpeg b/examples/excalidraw/with-nextjs/public/images/rocket.jpeg
similarity index 100%
rename from packages/excalidraw/example/public/images/rocket.jpeg
rename to examples/excalidraw/with-nextjs/public/images/rocket.jpeg
diff --git a/examples/excalidraw/with-nextjs/src/app/favicon.ico b/examples/excalidraw/with-nextjs/src/app/favicon.ico
new file mode 100644
index 0000000000..718d6fea48
Binary files /dev/null and b/examples/excalidraw/with-nextjs/src/app/favicon.ico differ
diff --git a/examples/excalidraw/with-nextjs/src/app/layout.tsx b/examples/excalidraw/with-nextjs/src/app/layout.tsx
new file mode 100644
index 0000000000..225b6038d7
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/src/app/layout.tsx
@@ -0,0 +1,11 @@
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/examples/excalidraw/with-nextjs/src/app/page.tsx b/examples/excalidraw/with-nextjs/src/app/page.tsx
new file mode 100644
index 0000000000..bc8c98fcff
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/src/app/page.tsx
@@ -0,0 +1,23 @@
+import dynamic from "next/dynamic";
+import "../common.scss";
+
+// Since client components get prerenderd on server as well hence importing the excalidraw stuff dynamically
+// with ssr false
+const ExcalidrawWithClientOnly = dynamic(
+ async () => (await import("../excalidrawWrapper")).default,
+ {
+ ssr: false,
+ },
+);
+
+export default function Page() {
+ return (
+ <>
+ Switch to Pages router
+ App Router
+
+ {/* @ts-expect-error - https://github.com/vercel/next.js/issues/42292 */}
+
+ >
+ );
+}
diff --git a/examples/excalidraw/with-nextjs/src/common.scss b/examples/excalidraw/with-nextjs/src/common.scss
new file mode 100644
index 0000000000..1a77600a92
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/src/common.scss
@@ -0,0 +1,15 @@
+* {
+ box-sizing: border-box;
+ font-family: sans-serif;
+}
+
+a {
+ color: #1c7ed6;
+ font-size: 20px;
+ text-decoration: none;
+ font-weight: 550;
+}
+
+.page-title {
+ text-align: center;
+}
diff --git a/examples/excalidraw/with-nextjs/src/excalidrawWrapper.tsx b/examples/excalidraw/with-nextjs/src/excalidrawWrapper.tsx
new file mode 100644
index 0000000000..40af9f0cce
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/src/excalidrawWrapper.tsx
@@ -0,0 +1,22 @@
+"use client";
+import * as excalidrawLib from "@excalidraw/excalidraw";
+import { Excalidraw } from "@excalidraw/excalidraw";
+import App from "../../components/App";
+
+import "@excalidraw/excalidraw/index.css";
+
+const ExcalidrawWrapper: React.FC = () => {
+ return (
+ <>
+ {}}
+ excalidrawLib={excalidrawLib}
+ >
+
+
+ >
+ );
+};
+
+export default ExcalidrawWrapper;
diff --git a/examples/excalidraw/with-nextjs/src/pages/excalidraw-in-pages.tsx b/examples/excalidraw/with-nextjs/src/pages/excalidraw-in-pages.tsx
new file mode 100644
index 0000000000..527a346b94
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/src/pages/excalidraw-in-pages.tsx
@@ -0,0 +1,22 @@
+import dynamic from "next/dynamic";
+import "../common.scss";
+
+// Since client components get prerenderd on server as well hence importing the excalidraw stuff dynamically
+// with ssr false
+const Excalidraw = dynamic(
+ async () => (await import("../excalidrawWrapper")).default,
+ {
+ ssr: false,
+ },
+);
+
+export default function Page() {
+ return (
+ <>
+ Switch to App router
+ Pages Router
+ {/* @ts-expect-error - https://github.com/vercel/next.js/issues/42292 */}
+
+ >
+ );
+}
diff --git a/examples/excalidraw/with-nextjs/tsconfig.json b/examples/excalidraw/with-nextjs/tsconfig.json
new file mode 100644
index 0000000000..09ae73d2e0
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "forceConsistentCasingInFileNames": true
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "build/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/examples/excalidraw/with-nextjs/vercel.json b/examples/excalidraw/with-nextjs/vercel.json
new file mode 100644
index 0000000000..bd885f4a5f
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/vercel.json
@@ -0,0 +1,3 @@
+{
+ "outputDirectory": "build"
+}
diff --git a/examples/excalidraw/with-nextjs/yarn.lock b/examples/excalidraw/with-nextjs/yarn.lock
new file mode 100644
index 0000000000..0072235c0f
--- /dev/null
+++ b/examples/excalidraw/with-nextjs/yarn.lock
@@ -0,0 +1,252 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@excalidraw/excalidraw@workspace:^":
+ version "0.17.2"
+ resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.17.2.tgz#9a636a1e6bb3c88c5883347d3a7e75e9cce8ab96"
+ integrity sha512-7pqUWD8+mPjDhF4XxG3gw4rvE2JGaLW3Vss5UZfTbITPxAtFaGEc1K081bncitnaYhUwN9ENJE0i87QB3poDwQ==
+
+"@next/env@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/env/-/env-14.0.4.tgz#d5cda0c4a862d70ae760e58c0cd96a8899a2e49a"
+ integrity sha512-irQnbMLbUNQpP1wcE5NstJtbuA/69kRfzBrpAD7Gsn8zm/CY6YQYc3HQBz8QPxwISG26tIm5afvvVbu508oBeQ==
+
+"@next/swc-darwin-arm64@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.0.4.tgz#27b1854c2cd04eb1d5e75081a1a792ad91526618"
+ integrity sha512-mF05E/5uPthWzyYDyptcwHptucf/jj09i2SXBPwNzbgBNc+XnwzrL0U6BmPjQeOL+FiB+iG1gwBeq7mlDjSRPg==
+
+"@next/swc-darwin-x64@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.0.4.tgz#9940c449e757d0ee50bb9e792d2600cc08a3eb3b"
+ integrity sha512-IZQ3C7Bx0k2rYtrZZxKKiusMTM9WWcK5ajyhOZkYYTCc8xytmwSzR1skU7qLgVT/EY9xtXDG0WhY6fyujnI3rw==
+
+"@next/swc-linux-arm64-gnu@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.0.4.tgz#0eafd27c8587f68ace7b4fa80695711a8434de21"
+ integrity sha512-VwwZKrBQo/MGb1VOrxJ6LrKvbpo7UbROuyMRvQKTFKhNaXjUmKTu7wxVkIuCARAfiI8JpaWAnKR+D6tzpCcM4w==
+
+"@next/swc-linux-arm64-musl@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.0.4.tgz#2b0072adb213f36dada5394ea67d6e82069ae7dd"
+ integrity sha512-8QftwPEW37XxXoAwsn+nXlodKWHfpMaSvt81W43Wh8dv0gkheD+30ezWMcFGHLI71KiWmHK5PSQbTQGUiidvLQ==
+
+"@next/swc-linux-x64-gnu@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.0.4.tgz#68c67d20ebc8e3f6ced6ff23a4ba2a679dbcec32"
+ integrity sha512-/s/Pme3VKfZAfISlYVq2hzFS8AcAIOTnoKupc/j4WlvF6GQ0VouS2Q2KEgPuO1eMBwakWPB1aYFIA4VNVh667A==
+
+"@next/swc-linux-x64-musl@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.0.4.tgz#67cd81b42fb2caf313f7992fcf6d978af55a1247"
+ integrity sha512-m8z/6Fyal4L9Bnlxde5g2Mfa1Z7dasMQyhEhskDATpqr+Y0mjOBZcXQ7G5U+vgL22cI4T7MfvgtrM2jdopqWaw==
+
+"@next/swc-win32-arm64-msvc@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.0.4.tgz#be06585906b195d755ceda28f33c633e1443f1a3"
+ integrity sha512-7Wv4PRiWIAWbm5XrGz3D8HUkCVDMMz9igffZG4NB1p4u1KoItwx9qjATHz88kwCEal/HXmbShucaslXCQXUM5w==
+
+"@next/swc-win32-ia32-msvc@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.0.4.tgz#e76cabefa9f2d891599c3d85928475bd8d3f6600"
+ integrity sha512-zLeNEAPULsl0phfGb4kdzF/cAVIfaC7hY+kt0/d+y9mzcZHsMS3hAS829WbJ31DkSlVKQeHEjZHIdhN+Pg7Gyg==
+
+"@next/swc-win32-x64-msvc@14.0.4":
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.0.4.tgz#e74892f1a9ccf41d3bf5979ad6d3d77c07b9cba1"
+ integrity sha512-yEh2+R8qDlDCjxVpzOTEpBLQTEFAcP2A8fUFLaWNap9GitYKkKv1//y2S6XY6zsR4rCOPRpU7plYDR+az2n30A==
+
+"@swc/helpers@0.5.2":
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.2.tgz#85ea0c76450b61ad7d10a37050289eded783c27d"
+ integrity sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==
+ dependencies:
+ tslib "^2.4.0"
+
+"@types/node@^20":
+ version "20.11.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.0.tgz#8e0b99e70c0c1ade1a86c4a282f7b7ef87c9552f"
+ integrity sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==
+ dependencies:
+ undici-types "~5.26.4"
+
+"@types/prop-types@*":
+ version "15.7.11"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563"
+ integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==
+
+"@types/react-dom@^18":
+ version "18.2.18"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.18.tgz#16946e6cd43971256d874bc3d0a72074bb8571dd"
+ integrity sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*", "@types/react@^18":
+ version "18.2.47"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.47.tgz#85074b27ab563df01fbc3f68dc64bf7050b0af40"
+ integrity sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
+"@types/scheduler@*":
+ version "0.16.8"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff"
+ integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==
+
+busboy@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
+ integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
+ dependencies:
+ streamsearch "^1.1.0"
+
+caniuse-lite@^1.0.30001406:
+ version "1.0.30001576"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz#893be772cf8ee6056d6c1e2d07df365b9ec0a5c4"
+ integrity sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==
+
+client-only@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
+ integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
+
+csstype@^3.0.2:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+ integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
+graceful-fs@^4.1.2, graceful-fs@^4.2.11:
+ version "4.2.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+"js-tokens@^3.0.0 || ^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+loose-envify@^1.1.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+nanoid@^3.3.6:
+ version "3.3.7"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
+ integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
+
+next@14.0.4:
+ version "14.0.4"
+ resolved "https://registry.yarnpkg.com/next/-/next-14.0.4.tgz#bf00b6f835b20d10a5057838fa2dfced1d0d84dc"
+ integrity sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==
+ dependencies:
+ "@next/env" "14.0.4"
+ "@swc/helpers" "0.5.2"
+ busboy "1.6.0"
+ caniuse-lite "^1.0.30001406"
+ graceful-fs "^4.2.11"
+ postcss "8.4.31"
+ styled-jsx "5.1.1"
+ watchpack "2.4.0"
+ optionalDependencies:
+ "@next/swc-darwin-arm64" "14.0.4"
+ "@next/swc-darwin-x64" "14.0.4"
+ "@next/swc-linux-arm64-gnu" "14.0.4"
+ "@next/swc-linux-arm64-musl" "14.0.4"
+ "@next/swc-linux-x64-gnu" "14.0.4"
+ "@next/swc-linux-x64-musl" "14.0.4"
+ "@next/swc-win32-arm64-msvc" "14.0.4"
+ "@next/swc-win32-ia32-msvc" "14.0.4"
+ "@next/swc-win32-x64-msvc" "14.0.4"
+
+path2d-polyfill@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz#24c554a738f42700d6961992bf5f1049672f2391"
+ integrity sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+postcss@8.4.31:
+ version "8.4.31"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
+ integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
+ dependencies:
+ nanoid "^3.3.6"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
+react-dom@^18:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+ integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
+ dependencies:
+ loose-envify "^1.1.0"
+ scheduler "^0.23.0"
+
+react@^18:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+ integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+ dependencies:
+ loose-envify "^1.1.0"
+
+scheduler@^0.23.0:
+ version "0.23.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+ integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+ dependencies:
+ loose-envify "^1.1.0"
+
+source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+streamsearch@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
+ integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
+
+styled-jsx@5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f"
+ integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==
+ dependencies:
+ client-only "0.0.1"
+
+tslib@^2.4.0:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
+ integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
+
+typescript@^5:
+ version "5.3.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
+ integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
+
+undici-types@~5.26.4:
+ version "5.26.5"
+ resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
+ integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
+
+watchpack@2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
+ integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
diff --git a/packages/excalidraw/example/public/index.html b/examples/excalidraw/with-script-in-browser/index.html
similarity index 67%
rename from packages/excalidraw/example/public/index.html
rename to examples/excalidraw/with-script-in-browser/index.html
index 0fbf45e9e3..a56d7f4216 100644
--- a/packages/excalidraw/example/public/index.html
+++ b/examples/excalidraw/with-script-in-browser/index.html
@@ -13,20 +13,20 @@
window.name = "codesandbox";
-
-
-
+
-
+