diff --git a/docs/package.json b/docs/package.json index 0ffbc251cc..bcd31a57ea 100644 --- a/docs/package.json +++ b/docs/package.json @@ -131,4 +131,4 @@ "tw-animate-css": "^1.4.0", "typescript": "^5.9.3" } -} +} \ No newline at end of file diff --git a/examples/01-basic/03-multi-column/package.json b/examples/01-basic/03-multi-column/package.json index 482c7409d8..590b6adc07 100644 --- a/examples/01-basic/03-multi-column/package.json +++ b/examples/01-basic/03-multi-column/package.json @@ -16,12 +16,12 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-multi-column": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/xl-multi-column": "latest" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/02-backend/03-s3/package.json b/examples/02-backend/03-s3/package.json index 32f939f061..2c2c249b08 100644 --- a/examples/02-backend/03-s3/package.json +++ b/examples/02-backend/03-s3/package.json @@ -11,8 +11,6 @@ "preview": "vite preview" }, "dependencies": { - "@aws-sdk/client-s3": "^3.609.0", - "@aws-sdk/s3-request-presigner": "^3.609.0", "@blocknote/ariakit": "latest", "@blocknote/core": "latest", "@blocknote/mantine": "latest", @@ -22,7 +20,9 @@ "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@aws-sdk/client-s3": "^3.609.0", + "@aws-sdk/s3-request-presigner": "^3.609.0" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/02-backend/04-rendering-static-documents/package.json b/examples/02-backend/04-rendering-static-documents/package.json index 14c1dd6aa7..41aeab8c5c 100644 --- a/examples/02-backend/04-rendering-static-documents/package.json +++ b/examples/02-backend/04-rendering-static-documents/package.json @@ -15,13 +15,13 @@ "@blocknote/core": "latest", "@blocknote/mantine": "latest", "@blocknote/react": "latest", - "@blocknote/server-util": "latest", "@blocknote/shadcn": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/server-util": "latest" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/03-ui-components/11-uppy-file-panel/package.json b/examples/03-ui-components/11-uppy-file-panel/package.json index d65faa9792..5523df0951 100644 --- a/examples/03-ui-components/11-uppy-file-panel/package.json +++ b/examples/03-ui-components/11-uppy-file-panel/package.json @@ -19,6 +19,8 @@ "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", + "react": "^19.2.3", + "react-dom": "^19.2.3", "@uppy/core": "^3.13.1", "@uppy/dashboard": "^3.9.1", "@uppy/drag-drop": "^3.1.1", @@ -30,8 +32,6 @@ "@uppy/status-bar": "^3.1.1", "@uppy/webcam": "^3.4.2", "@uppy/xhr-upload": "^3.4.0", - "react": "^19.2.3", - "react-dom": "^19.2.3", "react-icons": "^5.5.0" }, "devDependencies": { diff --git a/examples/03-ui-components/13-custom-ui/package.json b/examples/03-ui-components/13-custom-ui/package.json index 1c45dea04a..97585870e0 100644 --- a/examples/03-ui-components/13-custom-ui/package.json +++ b/examples/03-ui-components/13-custom-ui/package.json @@ -19,10 +19,10 @@ "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "@mui/icons-material": "^5.16.1", - "@mui/material": "^5.16.1", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@mui/icons-material": "^5.16.1", + "@mui/material": "^5.16.1" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/04-theming/06-code-block/package.json b/examples/04-theming/06-code-block/package.json index dd4d7472f5..037641e740 100644 --- a/examples/04-theming/06-code-block/package.json +++ b/examples/04-theming/06-code-block/package.json @@ -12,7 +12,6 @@ }, "dependencies": { "@blocknote/ariakit": "latest", - "@blocknote/code-block": "latest", "@blocknote/core": "latest", "@blocknote/mantine": "latest", "@blocknote/react": "latest", @@ -21,7 +20,8 @@ "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/code-block": "latest" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/04-theming/07-custom-code-block/.bnexample.json b/examples/04-theming/07-custom-code-block/.bnexample.json index 5776d2de67..84166710e3 100644 --- a/examples/04-theming/07-custom-code-block/.bnexample.json +++ b/examples/04-theming/07-custom-code-block/.bnexample.json @@ -5,10 +5,10 @@ "tags": ["Basic"], "dependencies": { "@blocknote/code-block": "latest", - "@shikijs/core": "^3.19.0", - "@shikijs/engine-javascript": "^3.19.0", - "@shikijs/langs-precompiled": "^3.19.0", - "@shikijs/themes": "^3.19.0", - "@shikijs/types": "^3.19.0" + "@shikijs/core": "^4", + "@shikijs/engine-javascript": "^4", + "@shikijs/langs-precompiled": "^4", + "@shikijs/themes": "^4", + "@shikijs/types": "^4" } } diff --git a/examples/04-theming/07-custom-code-block/package.json b/examples/04-theming/07-custom-code-block/package.json index df3d173512..d381ab18f1 100644 --- a/examples/04-theming/07-custom-code-block/package.json +++ b/examples/04-theming/07-custom-code-block/package.json @@ -12,7 +12,6 @@ }, "dependencies": { "@blocknote/ariakit": "latest", - "@blocknote/code-block": "latest", "@blocknote/core": "latest", "@blocknote/mantine": "latest", "@blocknote/react": "latest", @@ -20,13 +19,14 @@ "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "@blocknote/code-block": "latest", "@shikijs/core": "^4", "@shikijs/engine-javascript": "^4", "@shikijs/langs-precompiled": "^4", "@shikijs/themes": "^4", - "@shikijs/types": "^4", - "react": "^19.2.3", - "react-dom": "^19.2.3" + "@shikijs/types": "^4" }, "devDependencies": { "@types/react": "^19.2.3", @@ -34,4 +34,4 @@ "@vitejs/plugin-react": "^6.0.1", "vite": "^8.0.8" } -} +} \ No newline at end of file diff --git a/examples/05-interoperability/05-converting-blocks-to-pdf/package.json b/examples/05-interoperability/05-converting-blocks-to-pdf/package.json index a470b3892e..f402e292d7 100644 --- a/examples/05-interoperability/05-converting-blocks-to-pdf/package.json +++ b/examples/05-interoperability/05-converting-blocks-to-pdf/package.json @@ -16,14 +16,14 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-multi-column": "latest", - "@blocknote/xl-pdf-exporter": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "@react-pdf/renderer": "^4.3.0", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/xl-pdf-exporter": "latest", + "@blocknote/xl-multi-column": "latest", + "@react-pdf/renderer": "^4.3.0" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/05-interoperability/06-converting-blocks-to-docx/package.json b/examples/05-interoperability/06-converting-blocks-to-docx/package.json index dead9892bb..9f29639db1 100644 --- a/examples/05-interoperability/06-converting-blocks-to-docx/package.json +++ b/examples/05-interoperability/06-converting-blocks-to-docx/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-docx-exporter": "latest", - "@blocknote/xl-multi-column": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/xl-docx-exporter": "latest", + "@blocknote/xl-multi-column": "latest" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/05-interoperability/07-converting-blocks-to-odt/package.json b/examples/05-interoperability/07-converting-blocks-to-odt/package.json index afc26ae220..4c30dd0d65 100644 --- a/examples/05-interoperability/07-converting-blocks-to-odt/package.json +++ b/examples/05-interoperability/07-converting-blocks-to-odt/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-multi-column": "latest", - "@blocknote/xl-odt-exporter": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/xl-odt-exporter": "latest", + "@blocknote/xl-multi-column": "latest" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/05-interoperability/08-converting-blocks-to-react-email/package.json b/examples/05-interoperability/08-converting-blocks-to-react-email/package.json index d5a60f1f1e..15b4292fd7 100644 --- a/examples/05-interoperability/08-converting-blocks-to-react-email/package.json +++ b/examples/05-interoperability/08-converting-blocks-to-react-email/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-email-exporter": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "@react-email/render": "^2.0.4", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/xl-email-exporter": "latest", + "@react-email/render": "^2.0.4" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/07-collaboration/02-liveblocks/package.json b/examples/07-collaboration/02-liveblocks/package.json index 8da1946fac..52e5f94320 100644 --- a/examples/07-collaboration/02-liveblocks/package.json +++ b/examples/07-collaboration/02-liveblocks/package.json @@ -16,16 +16,16 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@liveblocks/client": "^3.17.0", - "@liveblocks/react": "^3.17.0", - "@liveblocks/react-blocknote": "^3.17.0", - "@liveblocks/react-tiptap": "^3.17.0", - "@liveblocks/react-ui": "^3.17.0", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", "react": "^19.2.3", "react-dom": "^19.2.3", + "@liveblocks/client": "^3.17.0", + "@liveblocks/react": "^3.17.0", + "@liveblocks/react-blocknote": "^3.17.0", + "@liveblocks/react-tiptap": "^3.17.0", + "@liveblocks/react-ui": "^3.17.0", "yjs": "^13.6.27" }, "devDependencies": { diff --git a/examples/07-collaboration/03-y-sweet/package.json b/examples/07-collaboration/03-y-sweet/package.json index 8270d8195d..e1dcc579ca 100644 --- a/examples/07-collaboration/03-y-sweet/package.json +++ b/examples/07-collaboration/03-y-sweet/package.json @@ -19,9 +19,9 @@ "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "@y-sweet/react": "^0.6.3", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@y-sweet/react": "^0.6.3" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/07-collaboration/05-comments/package.json b/examples/07-collaboration/05-comments/package.json index 7f675d3e2d..e1902158f5 100644 --- a/examples/07-collaboration/05-comments/package.json +++ b/examples/07-collaboration/05-comments/package.json @@ -19,9 +19,9 @@ "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "@y-sweet/react": "^0.6.3", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@y-sweet/react": "^0.6.3" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/07-collaboration/09-comments-testing/.bnexample.json b/examples/07-collaboration/09-comments-testing/.bnexample.json new file mode 100644 index 0000000000..5d7d986420 --- /dev/null +++ b/examples/07-collaboration/09-comments-testing/.bnexample.json @@ -0,0 +1,9 @@ +{ + "playground": true, + "docs": false, + "author": "matthewlipski", + "tags": ["Advanced", "Comments", "Testing"], + "dependencies": { + "yjs": "^13.6.27" + } +} diff --git a/examples/07-collaboration/09-comments-testing/README.md b/examples/07-collaboration/09-comments-testing/README.md new file mode 100644 index 0000000000..b59f2ecd1b --- /dev/null +++ b/examples/07-collaboration/09-comments-testing/README.md @@ -0,0 +1,3 @@ +# Comments Testing + +A minimal comments example used for end-to-end testing. Uses a local Y.Doc (no collaboration provider) with a single hardcoded editor user. diff --git a/examples/07-collaboration/09-comments-testing/index.html b/examples/07-collaboration/09-comments-testing/index.html new file mode 100644 index 0000000000..f50976be79 --- /dev/null +++ b/examples/07-collaboration/09-comments-testing/index.html @@ -0,0 +1,14 @@ + + + + + Comments Testing + + + +
+ + + diff --git a/examples/07-collaboration/09-comments-testing/main.tsx b/examples/07-collaboration/09-comments-testing/main.tsx new file mode 100644 index 0000000000..677c7f7eed --- /dev/null +++ b/examples/07-collaboration/09-comments-testing/main.tsx @@ -0,0 +1,11 @@ +// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY +import React from "react"; +import { createRoot } from "react-dom/client"; +import App from "./src/App.jsx"; + +const root = createRoot(document.getElementById("root")!); +root.render( + + + +); diff --git a/examples/07-collaboration/09-comments-testing/package.json b/examples/07-collaboration/09-comments-testing/package.json new file mode 100644 index 0000000000..070af0d48e --- /dev/null +++ b/examples/07-collaboration/09-comments-testing/package.json @@ -0,0 +1,32 @@ +{ + "name": "@blocknote/example-collaboration-comments-testing", + "description": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", + "type": "module", + "private": true, + "version": "0.12.4", + "scripts": { + "start": "vite", + "dev": "vite", + "build:prod": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@blocknote/ariakit": "latest", + "@blocknote/core": "latest", + "@blocknote/mantine": "latest", + "@blocknote/react": "latest", + "@blocknote/shadcn": "latest", + "@mantine/core": "^8.3.11", + "@mantine/hooks": "^8.3.11", + "@mantine/utils": "^6.0.22", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "yjs": "^13.6.27" + }, + "devDependencies": { + "@types/react": "^19.2.3", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "vite": "^8.0.8" + } +} \ No newline at end of file diff --git a/examples/07-collaboration/09-comments-testing/src/App.tsx b/examples/07-collaboration/09-comments-testing/src/App.tsx new file mode 100644 index 0000000000..3bada358c1 --- /dev/null +++ b/examples/07-collaboration/09-comments-testing/src/App.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { + CommentsExtension, + DefaultThreadStoreAuth, + YjsThreadStore, +} from "@blocknote/core/comments"; +import { BlockNoteView } from "@blocknote/mantine"; +import "@blocknote/mantine/style.css"; +import { useCreateBlockNote } from "@blocknote/react"; +import { useMemo } from "react"; +import * as Y from "yjs"; + +const USER = { + id: "1", + username: "John Doe", + avatarUrl: "https://placehold.co/100x100?text=John", + role: "editor" as const, +}; + +async function resolveUsers(userIds: string[]) { + return [USER].filter((user) => userIds.includes(user.id)); +} + +export default function App() { + const doc = useMemo(() => new Y.Doc(), []); + + const threadStore = useMemo(() => { + return new YjsThreadStore( + USER.id, + doc.getMap("threads"), + new DefaultThreadStoreAuth(USER.id, USER.role), + ); + }, [doc]); + + const editor = useCreateBlockNote( + { + extensions: [CommentsExtension({ threadStore, resolveUsers })], + }, + [threadStore], + ); + + return ; +} diff --git a/examples/07-collaboration/09-comments-testing/tsconfig.json b/examples/07-collaboration/09-comments-testing/tsconfig.json new file mode 100644 index 0000000000..dbe3e6f62d --- /dev/null +++ b/examples/07-collaboration/09-comments-testing/tsconfig.json @@ -0,0 +1,36 @@ +{ + "__comment": "AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": [ + "DOM", + "DOM.Iterable", + "ESNext" + ], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "composite": true + }, + "include": [ + "." + ], + "__ADD_FOR_LOCAL_DEV_references": [ + { + "path": "../../../packages/core/" + }, + { + "path": "../../../packages/react/" + } + ] +} \ No newline at end of file diff --git a/examples/07-collaboration/09-comments-testing/vite.config.ts b/examples/07-collaboration/09-comments-testing/vite.config.ts new file mode 100644 index 0000000000..f62ab20bc2 --- /dev/null +++ b/examples/07-collaboration/09-comments-testing/vite.config.ts @@ -0,0 +1,32 @@ +// AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY +import react from "@vitejs/plugin-react"; +import * as fs from "fs"; +import * as path from "path"; +import { defineConfig } from "vite"; +// import eslintPlugin from "vite-plugin-eslint"; +// https://vitejs.dev/config/ +export default defineConfig((conf) => ({ + plugins: [react()], + optimizeDeps: {}, + build: { + sourcemap: true, + }, + resolve: { + alias: + conf.command === "build" || + !fs.existsSync(path.resolve(__dirname, "../../packages/core/src")) + ? {} + : ({ + // Comment out the lines below to load a built version of blocknote + // or, keep as is to load live from sources with live reload working + "@blocknote/core": path.resolve( + __dirname, + "../../packages/core/src/" + ), + "@blocknote/react": path.resolve( + __dirname, + "../../packages/react/src/" + ), + } as any), + }, +})); diff --git a/examples/08-extensions/01-tiptap-arrow-conversion/package.json b/examples/08-extensions/01-tiptap-arrow-conversion/package.json index 5bca11ea17..b1055a283f 100644 --- a/examples/08-extensions/01-tiptap-arrow-conversion/package.json +++ b/examples/08-extensions/01-tiptap-arrow-conversion/package.json @@ -19,9 +19,9 @@ "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "@tiptap/core": "^3.13.0", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@tiptap/core": "^3.13.0" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/09-ai/01-minimal/package.json b/examples/09-ai/01-minimal/package.json index 92c27364e7..d78b179eb4 100644 --- a/examples/09-ai/01-minimal/package.json +++ b/examples/09-ai/01-minimal/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-ai": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "ai": "^6.0.5", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/xl-ai": "latest", + "ai": "^6.0.5" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/09-ai/02-playground/package.json b/examples/09-ai/02-playground/package.json index ed3c58dab8..7824de1c5b 100644 --- a/examples/09-ai/02-playground/package.json +++ b/examples/09-ai/02-playground/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-ai": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "ai": "^6.0.5", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/xl-ai": "latest", + "ai": "^6.0.5" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/09-ai/03-custom-ai-menu-items/package.json b/examples/09-ai/03-custom-ai-menu-items/package.json index fb628a4aa9..d6a2573f8e 100644 --- a/examples/09-ai/03-custom-ai-menu-items/package.json +++ b/examples/09-ai/03-custom-ai-menu-items/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-ai": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "ai": "^6.0.5", "react": "^19.2.3", "react-dom": "^19.2.3", + "@blocknote/xl-ai": "latest", + "ai": "^6.0.5", "react-icons": "^5.5.0" }, "devDependencies": { diff --git a/examples/09-ai/04-with-collaboration/package.json b/examples/09-ai/04-with-collaboration/package.json index 75daec4c9f..ef36c46e88 100644 --- a/examples/09-ai/04-with-collaboration/package.json +++ b/examples/09-ai/04-with-collaboration/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-ai": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "ai": "^6.0.5", "react": "^19.2.3", "react-dom": "^19.2.3", + "@blocknote/xl-ai": "latest", + "ai": "^6.0.5", "y-partykit": "^0.0.25", "yjs": "^13.6.27" }, diff --git a/examples/09-ai/05-manual-execution/package.json b/examples/09-ai/05-manual-execution/package.json index a1b130faa8..dd5e66ef92 100644 --- a/examples/09-ai/05-manual-execution/package.json +++ b/examples/09-ai/05-manual-execution/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-ai": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "ai": "^6.0.5", "react": "^19.2.3", "react-dom": "^19.2.3", + "@blocknote/xl-ai": "latest", + "ai": "^6.0.5", "y-partykit": "^0.0.25", "yjs": "^13.6.27" }, diff --git a/examples/09-ai/06-client-side-transport/package.json b/examples/09-ai/06-client-side-transport/package.json index 8cc1777300..64b09bc543 100644 --- a/examples/09-ai/06-client-side-transport/package.json +++ b/examples/09-ai/06-client-side-transport/package.json @@ -11,19 +11,19 @@ "preview": "vite preview" }, "dependencies": { - "@ai-sdk/groq": "^3.0.2", "@blocknote/ariakit": "latest", "@blocknote/core": "latest", "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-ai": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "ai": "^6.0.5", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@ai-sdk/groq": "^3.0.2", + "@blocknote/xl-ai": "latest", + "ai": "^6.0.5" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/examples/09-ai/07-server-persistence/package.json b/examples/09-ai/07-server-persistence/package.json index 0ee494038b..0ac67163a2 100644 --- a/examples/09-ai/07-server-persistence/package.json +++ b/examples/09-ai/07-server-persistence/package.json @@ -16,13 +16,13 @@ "@blocknote/mantine": "latest", "@blocknote/react": "latest", "@blocknote/shadcn": "latest", - "@blocknote/xl-ai": "latest", "@mantine/core": "^8.3.11", "@mantine/hooks": "^8.3.11", "@mantine/utils": "^6.0.22", - "ai": "^6.0.5", "react": "^19.2.3", - "react-dom": "^19.2.3" + "react-dom": "^19.2.3", + "@blocknote/xl-ai": "latest", + "ai": "^6.0.5" }, "devDependencies": { "@types/react": "^19.2.3", diff --git a/packages/core/src/comments/extension.ts b/packages/core/src/comments/extension.ts index 4e8e566cef..515a740c34 100644 --- a/packages/core/src/comments/extension.ts +++ b/packages/core/src/comments/extension.ts @@ -306,6 +306,11 @@ export const CommentsExtension = createExtension( selectedThreadId: undefined, pendingComment: true, })); + // Use `editor.domElement` as `editor.focus()` doesn't do anything if + // the editor is non-editable. Editor needs to be focused as + // `showSelection` will otherwise trigger a selection update which + // triggers `stopPendingComment`. + editor.domElement?.focus(); editor .getExtension(ShowSelectionExtension) ?.showSelection(true, "comments"); diff --git a/packages/core/src/editor/managers/StyleManager.ts b/packages/core/src/editor/managers/StyleManager.ts index e03c46a6d1..e8ffa99881 100644 --- a/packages/core/src/editor/managers/StyleManager.ts +++ b/packages/core/src/editor/managers/StyleManager.ts @@ -169,7 +169,12 @@ export class StyleManager< const { from, to } = tr.selection; if (text) { - tr.insertText(text, from, to).addMark(from, from + text.length, mark); + const existingText = tr.doc.textBetween(from, to); + if (text !== existingText) { + tr.insertText(text, from, to); + } + + tr.addMark(from, from + text.length, mark); } else { tr.setSelection(TextSelection.create(tr.doc, to)).addMark( from, diff --git a/packages/core/src/extensions/LinkToolbar/LinkToolbar.ts b/packages/core/src/extensions/LinkToolbar/LinkToolbar.ts index 1a61d67d44..f190bc97e6 100644 --- a/packages/core/src/extensions/LinkToolbar/LinkToolbar.ts +++ b/packages/core/src/extensions/LinkToolbar/LinkToolbar.ts @@ -88,7 +88,12 @@ export const LinkToolbarExtension = createExtension(({ editor }) => { if (!range) { return; } - tr.insertText(text, range.from, range.to); + + const existingText = tr.doc.textBetween(range.from, range.to); + if (text !== existingText) { + tr.insertText(text, range.from, range.to); + } + tr.addMark( range.from, range.from + text.length, diff --git a/packages/dev-scripts/examples/template-react/package.json.template.tsx b/packages/dev-scripts/examples/template-react/package.json.template.tsx index a08cdc93ad..98c67f3e50 100644 --- a/packages/dev-scripts/examples/template-react/package.json.template.tsx +++ b/packages/dev-scripts/examples/template-react/package.json.template.tsx @@ -39,8 +39,8 @@ const template = (project: Project) => ({ : {}), "@types/react": "^19.2.3", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^4.7.0", - vite: "^5.4.20", + "@vitejs/plugin-react": "^6.0.1", + vite: "^8.0.8", ...(project.config?.devDependencies || {}), }, }); diff --git a/packages/react/src/components/Comments/FloatingComposer.tsx b/packages/react/src/components/Comments/FloatingComposer.tsx index 1cc72d634d..aa3e786dd9 100644 --- a/packages/react/src/components/Comments/FloatingComposer.tsx +++ b/packages/react/src/components/Comments/FloatingComposer.tsx @@ -1,4 +1,12 @@ -import { mergeCSSClasses } from "@blocknote/core"; +import { + BlockSchema, + DefaultBlockSchema, + DefaultInlineContentSchema, + DefaultStyleSchema, + InlineContentSchema, + mergeCSSClasses, + StyleSchema, +} from "@blocknote/core"; import { CommentsExtension } from "@blocknote/core/comments"; import { useComponentsContext } from "../../editor/ComponentsContext.js"; @@ -7,13 +15,21 @@ import { useExtension } from "../../hooks/useExtension.js"; import { useDictionary } from "../../i18n/dictionary.js"; import { CommentEditor } from "./CommentEditor.js"; import { defaultCommentEditorSchema } from "./defaultCommentEditorSchema.js"; +import { useBlockNoteEditor } from "../../hooks/useBlockNoteEditor.js"; +import { TextSelection } from "@tiptap/pm/state"; /** * The FloatingComposer component displays a comment editor "floating" card. * * It's used when the user highlights a parts of the document to create a new comment / thread. */ -export function FloatingComposer() { +export function FloatingComposer< + B extends BlockSchema = DefaultBlockSchema, + I extends InlineContentSchema = DefaultInlineContentSchema, + S extends StyleSchema = DefaultStyleSchema, +>() { + const editor = useBlockNoteEditor(); + const comments = useExtension(CommentsExtension); const Components = useComponentsContext()!; @@ -57,6 +73,12 @@ export function FloatingComposer() { }, }); comments.stopPendingComment(); + editor.transact((tr) => { + tr.setSelection( + TextSelection.create(tr.doc, tr.selection.to), + ); + }); + editor.focus(); }} > Save diff --git a/playground/src/examples.gen.tsx b/playground/src/examples.gen.tsx index 00a2e5afe5..5f219ff7fa 100644 --- a/playground/src/examples.gen.tsx +++ b/playground/src/examples.gen.tsx @@ -997,11 +997,11 @@ ], "dependencies": { "@blocknote/code-block": "latest", - "@shikijs/core": "^3.19.0", - "@shikijs/engine-javascript": "^3.19.0", - "@shikijs/langs-precompiled": "^3.19.0", - "@shikijs/themes": "^3.19.0", - "@shikijs/types": "^3.19.0" + "@shikijs/core": "^4", + "@shikijs/engine-javascript": "^4", + "@shikijs/langs-precompiled": "^4", + "@shikijs/themes": "^4", + "@shikijs/types": "^4" } as any }, "title": "Custom Code Block Theme & Language", @@ -1687,6 +1687,30 @@ "slug": "collaboration" }, "readme": "In this example, we can fork a document and edit it independently of other collaborators. Then, we can choose to merge the changes back into the original document, or discard the changes.\n\n**Try it out:** Open this page in a new browser tab or window to see it in action!\n\n**Relevant Docs:**\n\n- [Editor Setup](/docs/getting-started/editor-setup)" + }, + { + "projectSlug": "comments-testing", + "fullSlug": "collaboration/comments-testing", + "pathFromRoot": "examples/07-collaboration/09-comments-testing", + "config": { + "playground": true, + "docs": false, + "author": "matthewlipski", + "tags": [ + "Advanced", + "Comments", + "Testing" + ], + "dependencies": { + "yjs": "^13.6.27" + } as any + }, + "title": "Comments Testing", + "group": { + "pathFromRoot": "examples/07-collaboration", + "slug": "collaboration" + }, + "readme": "A minimal comments example used for end-to-end testing. Uses a local Y.Doc (no collaboration provider) with a single hardcoded editor user." } ] }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2cd8b1127..202a26aeda 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4006,6 +4006,55 @@ importers: specifier: ^8.0.8 version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + examples/07-collaboration/09-comments-testing: + dependencies: + '@blocknote/ariakit': + specifier: latest + version: link:../../../packages/ariakit + '@blocknote/core': + specifier: latest + version: link:../../../packages/core + '@blocknote/mantine': + specifier: latest + version: link:../../../packages/mantine + '@blocknote/react': + specifier: latest + version: link:../../../packages/react + '@blocknote/shadcn': + specifier: latest + version: link:../../../packages/shadcn + '@mantine/core': + specifier: ^8.3.11 + version: 8.3.18(@mantine/hooks@8.3.18(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@mantine/hooks': + specifier: ^8.3.11 + version: 8.3.18(react@19.2.5) + '@mantine/utils': + specifier: ^6.0.22 + version: 6.0.22(react@19.2.5) + react: + specifier: ^19.2.3 + version: 19.2.5 + react-dom: + specifier: ^19.2.3 + version: 19.2.5(react@19.2.5) + yjs: + specifier: ^13.6.27 + version: 13.6.30 + devDependencies: + '@types/react': + specifier: ^19.2.3 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(babel-plugin-react-compiler@1.0.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vite: + specifier: ^8.0.8 + version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.5)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + examples/08-extensions/01-tiptap-arrow-conversion: dependencies: '@blocknote/ariakit': diff --git a/tests/src/end-to-end/ariakit/ariakit.test.ts b/tests/src/end-to-end/ariakit/ariakit.test.ts index aa8c05c7d6..dd5fa0eccf 100644 --- a/tests/src/end-to-end/ariakit/ariakit.test.ts +++ b/tests/src/end-to-end/ariakit/ariakit.test.ts @@ -35,7 +35,9 @@ test.describe("Check Ariakit UI", () => { await page.keyboard.type("link"); await page.keyboard.press("Enter"); + await page.waitForTimeout(500); await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowRight"); await page.waitForTimeout(500); expect(await page.screenshot()).toMatchSnapshot("ariakit-link-toolbar.png"); diff --git a/tests/src/end-to-end/comments/comments.test.ts b/tests/src/end-to-end/comments/comments.test.ts new file mode 100644 index 0000000000..db29498359 --- /dev/null +++ b/tests/src/end-to-end/comments/comments.test.ts @@ -0,0 +1,35 @@ +import { expect } from "@playwright/test"; +import { test } from "../../setup/setupScript.js"; +import { COMMENTS_URL, LINK_BUTTON_SELECTOR } from "../../utils/const.js"; +import { focusOnEditor } from "../../utils/editor.js"; + +test.beforeEach(async ({ page }) => { + await page.goto(COMMENTS_URL); +}); + +test.describe("Check Comments functionality", () => { + test("Should preserve existing comments when adding a link", async ({ + page, + }) => { + await focusOnEditor(page); + + await page.keyboard.type("hello"); + await page.locator("text=hello").dblclick(); + + await page.click('[data-test="addcomment"]'); + await page.waitForSelector(".bn-thread"); + + await page.keyboard.type("test comment"); + await page.click('button[data-test="save"]'); + + await page.locator("span.bn-thread-mark").first().dblclick(); + + await expect(page.locator(LINK_BUTTON_SELECTOR)).toBeVisible(); + await page.click(LINK_BUTTON_SELECTOR); + + await page.keyboard.type("https://example.com"); + await page.keyboard.press("Enter"); + + await expect(await page.locator("span.bn-thread-mark")).toBeVisible(); + }); +}); diff --git a/tests/src/end-to-end/shadcn/shadcn.test.ts b/tests/src/end-to-end/shadcn/shadcn.test.ts index c4a46ed8cc..fe1a2e385f 100644 --- a/tests/src/end-to-end/shadcn/shadcn.test.ts +++ b/tests/src/end-to-end/shadcn/shadcn.test.ts @@ -35,7 +35,9 @@ test.describe("Check ShadCN UI", () => { await page.keyboard.type("link"); await page.keyboard.press("Enter"); + await page.waitForTimeout(500); await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowRight"); await page.waitForTimeout(700); expect(await page.screenshot()).toMatchSnapshot("shadcn-link-toolbar.png"); diff --git a/tests/src/end-to-end/theming/theming.test.ts b/tests/src/end-to-end/theming/theming.test.ts index 8bc75a6fab..1647cda1a9 100644 --- a/tests/src/end-to-end/theming/theming.test.ts +++ b/tests/src/end-to-end/theming/theming.test.ts @@ -46,7 +46,9 @@ test.describe("Check Dark Theme is Automatically Applied", () => { await page.waitForTimeout(500); await page.keyboard.type("link"); await page.keyboard.press("Enter"); + await page.waitForTimeout(500); await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowRight"); await page.waitForTimeout(500); expect(await page.screenshot()).toMatchSnapshot("dark-link-toolbar.png"); diff --git a/tests/src/utils/const.ts b/tests/src/utils/const.ts index 61cedc194d..b04b77d6a2 100644 --- a/tests/src/utils/const.ts +++ b/tests/src/utils/const.ts @@ -43,6 +43,10 @@ export const ALERT_BLOCK_URL = !process.env.RUN_IN_DOCKER ? `http://localhost:${PORT}/custom-schema/alert-block?hideMenu` : `http://host.docker.internal:${PORT}/custom-schema/alert-block?hideMenu`; +export const COMMENTS_URL = !process.env.RUN_IN_DOCKER + ? `http://localhost:${PORT}/collaboration/comments-testing?hideMenu` + : `http://host.docker.internal:${PORT}/collaboration/comments-testing?hideMenu`; + export const PASTE_ZONE_SELECTOR = "#pasteZone"; export const EDITOR_SELECTOR = `.bn-editor`;