Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions docs/app/demo/_components/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { uploadFile_DEV_ONLY } from "@blocknote/core";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't really want this exported, I get that it is a shared util, but not one that needs to be actually packaged. Just copy-paste in the places that need it


export const HARDCODED_USERS = [
{
id: "user-1",
Expand Down Expand Up @@ -64,17 +66,9 @@ export async function resolveUsers(userIds: string[]) {
return HARDCODED_USERS.filter((user) => userIds.includes(user.id));
}

// Uploads a file to tmpfiles.org and returns the URL to the uploaded file.
// "Uploads" a file using BlockNote's dev-only helper, which encodes it as a
// base64 data URL. In a real app you'd replace this with an upload to your own
// backend that returns a URL to the stored file.
export async function uploadFile(file: File) {
const body = new FormData();
body.append("file", file);

const ret = await fetch("https://tmpfiles.org/api/v1/upload", {
method: "POST",
body: body,
});
return (await ret.json()).data.url.replace(
"tmpfiles.org/",
"tmpfiles.org/dl/",
);
return uploadFile_DEV_ONLY(file);
}
2 changes: 1 addition & 1 deletion docs/content/docs/react/components/image-toolbar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type uploadFile = (file: File) => Promise<string>;

`returns:` A `Promise`, which resolves to the URL that the image can be accessed at.

You can use the provided `uploadToTempFilesOrg` function to as a starting point, which uploads files to [tmpfiles.org](https://tmpfiles.org/). However, it's not recommended to use this in a production environment - you should use your own backend:
You can use the provided `uploadFile_DEV_ONLY` function as a starting point, which encodes files as base64 data URLs. However, it's only meant for development - in production you should use your own backend:

<Example name="backend/file-uploading" />

Expand Down
4 changes: 2 additions & 2 deletions examples/01-basic/testing/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { uploadToTmpFilesDotOrg_DEV_ONLY } from "@blocknote/core";
import { uploadFile_DEV_ONLY } from "@blocknote/core";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
Expand All @@ -7,7 +7,7 @@ import { useCreateBlockNote } from "@blocknote/react";
export default function App() {
// Creates a new editor instance.
const editor = useCreateBlockNote({
uploadFile: uploadToTmpFilesDotOrg_DEV_ONLY,
uploadFile: uploadFile_DEV_ONLY,
});

// Renders the editor instance using a React component.
Expand Down
2 changes: 1 addition & 1 deletion examples/02-backend/01-file-uploading/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Upload Files

This example allows users to upload files and use them in the editor. The files are uploaded to [/TMP/Files](https://tmpfiles.org/), and can be used for File, Image, Video, and Audio blocks.
This example allows users to upload files and use them in the editor. For simplicity, files are encoded as data URLs rather than uploaded to a server, but you'd swap the `uploadFile` function for an upload to your own backend. The uploaded files can be used for File, Image, Video, and Audio blocks.

**Try it out:** Click the "Add Image" button and see there's now an "Upload" tab in the toolbar!

Expand Down
19 changes: 7 additions & 12 deletions examples/02-backend/01-file-uploading/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import { uploadFile_DEV_ONLY } from "@blocknote/core";
import "@blocknote/core/fonts/inter.css";
import { useCreateBlockNote } from "@blocknote/react";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";

// Uploads a file to tmpfiles.org and returns the URL to the uploaded file.
// "Uploads" a file using BlockNote's dev-only helper, which encodes it as a
// base64 data URL. We add a short delay first to simulate the latency of a real
// server upload. In a real app you'd replace this with an upload to your own
// backend that returns a URL to the stored file.
async function uploadFile(file: File) {
const body = new FormData();
body.append("file", file);

const ret = await fetch("https://tmpfiles.org/api/v1/upload", {
method: "POST",
body: body,
});
return (await ret.json()).data.url.replace(
"tmpfiles.org/",
"tmpfiles.org/dl/",
);
await new Promise((resolve) => setTimeout(resolve, 1000));
return uploadFile_DEV_ONLY(file);
}

export default function App() {
Expand Down
71 changes: 19 additions & 52 deletions examples/03-ui-components/11-uppy-file-panel/src/UppyFilePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { uploadFile_DEV_ONLY } from "@blocknote/core";
import { FilePanelProps, useBlockNoteEditor } from "@blocknote/react";
import Uppy, { UploadSuccessCallback } from "@uppy/core";
import Uppy, { UploadCompleteCallback } from "@uppy/core";
import "@uppy/core/dist/style.min.css";
import "@uppy/dashboard/dist/style.min.css";
import { Dashboard } from "@uppy/react";
import XHR from "@uppy/xhr-upload";
import { useEffect } from "react";

// Image editor plugin
Expand All @@ -25,58 +25,38 @@ const uppy = new Uppy()
// Instagram Dropbox etc.
.use(Webcam)
.use(ScreenCapture)
.use(ImageEditor)
.use(ImageEditor);

// Uses an XHR upload plugin to upload files to tmpfiles.org.
// You want to replace this with your own upload endpoint or Uppy Companion
// server.
.use(XHR, {
endpoint: "https://tmpfiles.org/api/v1/upload",
getResponseData(text, _resp) {
return {
url: JSON.parse(text).data.url.replace(
"tmpfiles.org/",
"tmpfiles.org/dl/",
),
};
},
});
// No uploader plugin is registered: for this demo we "upload" files with
// BlockNote's dev-only helper, which encodes them as base64 data URLs. In a real
// app you'd add an uploader like `@uppy/xhr-upload` pointing at your own backend
// or Uppy Companion server.

export function UppyFilePanel(props: FilePanelProps) {
const { blockId } = props;
const editor = useBlockNoteEditor();

useEffect(() => {
// Listen for successful tippy uploads, and then update the Block with the
// uploaded URL.
const handler: UploadSuccessCallback<Record<string, unknown>> = (
file,
response,
// Listen for completed Dashboard uploads, then update the Block with the
// uploaded file's URL.
const handler: UploadCompleteCallback<Record<string, unknown>> = async (
result,
) => {
if (!file) {
return;
}

if (file.source === "uploadFile") {
// Didn't originate from Dashboard, should be handled by `uploadFile`
return;
}
if (response.status === 200) {
const updateData = {
for (const file of result.successful) {
editor.updateBlock(blockId, {
props: {
name: file?.name,
url: response.uploadURL,
name: file.name,
url: await uploadFile_DEV_ONLY(file.data as File),
},
};
editor.updateBlock(blockId, updateData);
});

// File should be removed from the Uppy instance after upload.
uppy.removeFile(file.id);
}
};
uppy.on("upload-success", handler);
uppy.on("complete", handler);
return () => {
uppy.off("upload-success", handler);
uppy.off("complete", handler);
};
}, [blockId, editor]);

Expand All @@ -87,18 +67,5 @@ export function UppyFilePanel(props: FilePanelProps) {
// Implementation for the BlockNote `uploadFile` function.
// This function is used when for example, files are dropped into the editor.
export async function uploadFile(file: File) {
const id = uppy.addFile({
id: file.name,
name: file.name,
type: file.type,
data: file,
source: "uploadFile",
});

try {
const result = await uppy.upload();
return result.successful[0].response!.uploadURL!;
} finally {
uppy.removeFile(id);
}
return uploadFile_DEV_ONLY(file);
}
15 changes: 15 additions & 0 deletions packages/core/src/blocks/File/helpers/uploadFile_DEV_ONLY.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Encodes a file as a base64 data URL and returns it. This keeps the demo
* self-contained (no external upload host, so nothing can rate-limit or break
* it), at the cost of embedding the file directly in the document.
*
* @warning This function should only be used for development purposes, replace with your own backend!
*/
export const uploadFile_DEV_ONLY = async (file: File): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(file);
});
};

This file was deleted.

2 changes: 1 addition & 1 deletion packages/core/src/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export * from "./Code/helpers/parse/parsePreCode.js";
export * from "./Code/helpers/render/createCodeBlock.js";
export * from "./Code/helpers/toExternalHTML/createPreCode.js";
export * from "./ToggleWrapper/createToggleWrapper.js";
export * from "./File/helpers/uploadToTmpFilesDotOrg_DEV_ONLY.js";
export * from "./File/helpers/uploadFile_DEV_ONLY.js";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh we had this exported before? That is strange to me

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I still remove the export then? Or keep as it was?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove it

export * from "./PageBreak/getPageBreakSlashMenuItems.js";

export * from "./BlockNoteSchema.js";
Expand Down
2 changes: 1 addition & 1 deletion playground/src/examples.gen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ export const examples = {
slug: "backend",
},
readme:
'This example allows users to upload files and use them in the editor. The files are uploaded to [/TMP/Files](https://tmpfiles.org/), and can be used for File, Image, Video, and Audio blocks.\n\n**Try it out:** Click the "Add Image" button and see there\'s now an "Upload" tab in the toolbar!\n\n**Relevant Docs:**\n\n- [Editor Setup](/docs/getting-started/editor-setup)\n- [File Block](/docs/features/blocks/embeds#file)',
'This example allows users to upload files and use them in the editor. For simplicity, files are encoded as data URLs rather than uploaded to a server, but you\'d swap the `uploadFile` function for an upload to your own backend. The uploaded files can be used for File, Image, Video, and Audio blocks.\n\n**Try it out:** Click the "Add Image" button and see there\'s now an "Upload" tab in the toolbar!\n\n**Relevant Docs:**\n\n- [Editor Setup](/docs/getting-started/editor-setup)\n- [File Block](/docs/features/blocks/embeds#file)',
},
{
projectSlug: "saving-loading",
Expand Down
2 changes: 1 addition & 1 deletion tests/src/end-to-end/images/images.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe("Check Image Block and Toolbar functionality", () => {
type: "image/png",
});
await userEvent.upload(uploadInput, file);
await waitForSelector(`img[src^="https://tmpfiles.org/"]`);
await waitForSelector(`img[src^="data:"]`);
await sleep(500);

await userEvent.click(await waitForSelector(`img`));
Expand Down
4 changes: 2 additions & 2 deletions tests/src/unit/core/createTestEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
createCodeBlockSpec,
InlineContentSchema,
StyleSchema,
uploadToTmpFilesDotOrg_DEV_ONLY,
uploadFile_DEV_ONLY,
} from "@blocknote/core";
import { afterAll, beforeAll } from "vite-plus/test";

Expand Down Expand Up @@ -57,7 +57,7 @@ export const createTestEditor = <
headers: true,
},
trailingBlock: false,
uploadFile: uploadToTmpFilesDotOrg_DEV_ONLY,
uploadFile: uploadFile_DEV_ONLY,
}) as any;
editor.mount(div);
});
Expand Down
Loading