diff --git a/docs/content/docs/features/ai/custom-commands.mdx b/docs/content/docs/features/ai/custom-commands.mdx index de02f19743..75b5c029db 100644 --- a/docs/content/docs/features/ai/custom-commands.mdx +++ b/docs/content/docs/features/ai/custom-commands.mdx @@ -26,8 +26,8 @@ First, we define a new AI command. Let's create one that makes selected text mor ```tsx import { + AIExtension, AIMenuSuggestionItem, - getAIExtension, aiDocumentFormats, } from "@blocknote/xl-ai"; @@ -40,7 +40,7 @@ export const makeInformal = ( aliases: ["informal", "make informal", "casual"], icon: , onItemClick: async () => { - await getAIExtension(editor).invokeAI({ + await editor.getExtension(AIExtension)?.invokeAI({ // The prompt to send to the LLM: userPrompt: "Give the selected text a more informal (casual) tone", // Tell the LLM to specifically use the selected content as context (instead of the whole document) diff --git a/docs/content/docs/features/ai/reference.mdx b/docs/content/docs/features/ai/reference.mdx index 2bafd6b647..1197efcc6b 100644 --- a/docs/content/docs/features/ai/reference.mdx +++ b/docs/content/docs/features/ai/reference.mdx @@ -52,14 +52,6 @@ type AIRequestHelpers = { }; ``` -## `getAIExtension` - -Use `getAIExtension` to retrieve the AI extension instance registered to the editor: - -```typescript -function getAIExtension(editor: BlockNoteEditor): AIExtension; -``` - ## `AIExtension` The `AIExtension` class is the main class for the AI extension. It exposes state and methods to interact with BlockNote's AI features. diff --git a/docs/content/docs/features/extensions.mdx b/docs/content/docs/features/extensions.mdx index e2fa8e904e..8b1dcd37c0 100644 --- a/docs/content/docs/features/extensions.mdx +++ b/docs/content/docs/features/extensions.mdx @@ -14,7 +14,7 @@ BlockNote includes an extensions system which lets you expand the editor's behav ## Creating an extension -An extension is an instance of the [`BlockNoteExtension`](https://github.com/TypeCellOS/BlockNote/blob/10cdbfb5f77ef82f3617c0fa1191e0bf5b7358c5/packages/core/src/editor/BlockNoteExtension.ts#L13) class. However, it's recommended for most use cases to create extensions using the `createBlockNoteExtension` function, rather than instanciating the class directly: +An extension is an instance of the [`BlockNoteExtension`](https://github.com/TypeCellOS/BlockNote/blob/10cdbfb5f77ef82f3617c0fa1191e0bf5b7358c5/packages/core/src/editor/BlockNoteExtension.ts#L13) class. However, it's recommended for most use cases to create extensions using the `createExtension` function, rather than instanciating the class directly: ```typescript type BlockNoteExtensionOptions = { @@ -43,10 +43,10 @@ const customBlockExtensionOptions: BlockNoteExtensionOptions = { tiptapExtensions: ..., } -const CustomExtension = createBlockNoteExtension(customBlockExtensionOptions); +const CustomExtension = createExtension(customBlockExtensionOptions); ``` -Let's go over the options that can be passed into `createBlockNoteExtension`: +Let's go over the options that can be passed into `createExtension`: `key:` The name of the extension. @@ -74,7 +74,7 @@ The `extensions` [editor option](/docs/reference/editor/overview#options) takes const editor = useCreateBlockNote({ extensions: [ // Add extensions here: - createBlockNoteExtension({ ... }) + createExtension({ ... }) ], }); ``` @@ -95,7 +95,7 @@ const createCustomBlock = createReactBlockSpec( } [ // Add extensions here: - createBlockNoteExtension({ ... }) + createExtension({ ... }) ], }); ``` diff --git a/examples/01-basic/03-multi-column/src/App.tsx b/examples/01-basic/03-multi-column/src/App.tsx index c126a9d2bf..3a0d10d8d9 100644 --- a/examples/01-basic/03-multi-column/src/App.tsx +++ b/examples/01-basic/03-multi-column/src/App.tsx @@ -1,8 +1,8 @@ import { BlockNoteSchema, combineByGroup, - filterSuggestionItems, } from "@blocknote/core"; +import { filterSuggestionItems } from "@blocknote/core/extensions"; import * as locales from "@blocknote/core/locales"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; diff --git a/examples/03-ui-components/04-side-menu-buttons/src/App.tsx b/examples/03-ui-components/04-side-menu-buttons/src/App.tsx index 8661c08baa..96ef099ef3 100644 --- a/examples/03-ui-components/04-side-menu-buttons/src/App.tsx +++ b/examples/03-ui-components/04-side-menu-buttons/src/App.tsx @@ -39,7 +39,7 @@ export default function App() { sideMenu={(props) => ( {/* Button which removes the hovered block. */} - + )} diff --git a/examples/03-ui-components/04-side-menu-buttons/src/RemoveBlockButton.tsx b/examples/03-ui-components/04-side-menu-buttons/src/RemoveBlockButton.tsx index c387f67f1e..774adc51b2 100644 --- a/examples/03-ui-components/04-side-menu-buttons/src/RemoveBlockButton.tsx +++ b/examples/03-ui-components/04-side-menu-buttons/src/RemoveBlockButton.tsx @@ -1,16 +1,20 @@ +import {} from "@blocknote/core"; +import { SideMenu } from "@blocknote/core/extensions"; import { - SideMenuProps, useBlockNoteEditor, useComponentsContext, + useExtension, } from "@blocknote/react"; import { MdDelete } from "react-icons/md"; // Custom Side Menu button to remove the hovered block. -export function RemoveBlockButton(props: SideMenuProps) { +export function RemoveBlockButton() { const editor = useBlockNoteEditor(); const Components = useComponentsContext()!; + const sideMenu = useExtension(SideMenu); + return ( { - editor.removeBlocks([props.block]); + editor.removeBlocks([sideMenu.store.state!.block]); }} /> } diff --git a/examples/03-ui-components/05-side-menu-drag-handle-items/src/App.tsx b/examples/03-ui-components/05-side-menu-drag-handle-items/src/App.tsx index 88d82aad2d..0ff3a07174 100644 --- a/examples/03-ui-components/05-side-menu-drag-handle-items/src/App.tsx +++ b/examples/03-ui-components/05-side-menu-drag-handle-items/src/App.tsx @@ -4,7 +4,6 @@ import "@blocknote/mantine/style.css"; import { BlockColorsItem, DragHandleMenu, - DragHandleMenuProps, RemoveBlockItem, SideMenu, SideMenuController, @@ -16,12 +15,12 @@ import { ResetBlockTypeItem } from "./ResetBlockTypeItem"; // To avoid rendering issues, it's good practice to define your custom drag // handle menu in a separate component, instead of inline within the `sideMenu` // prop of `SideMenuController`. -const CustomDragHandleMenu = (props: DragHandleMenuProps) => ( - - Delete - Colors +const CustomDragHandleMenu = () => ( + + Delete + Colors {/* Item which resets the hovered block's type. */} - Reset Type + Reset Type ); diff --git a/examples/03-ui-components/05-side-menu-drag-handle-items/src/ResetBlockTypeItem.tsx b/examples/03-ui-components/05-side-menu-drag-handle-items/src/ResetBlockTypeItem.tsx index 847c7b9f6c..fe446c205d 100644 --- a/examples/03-ui-components/05-side-menu-drag-handle-items/src/ResetBlockTypeItem.tsx +++ b/examples/03-ui-components/05-side-menu-drag-handle-items/src/ResetBlockTypeItem.tsx @@ -1,21 +1,26 @@ +import {} from "@blocknote/core"; +import { SideMenu } from "@blocknote/core/extensions"; import { - DragHandleMenuProps, useBlockNoteEditor, useComponentsContext, + useExtension, } from "@blocknote/react"; +import { ReactNode } from "react"; -export function ResetBlockTypeItem(props: DragHandleMenuProps) { +export function ResetBlockTypeItem(props: { children: ReactNode }) { const editor = useBlockNoteEditor(); const Components = useComponentsContext()!; + const sideMenu = useExtension(SideMenu); + return ( { - editor.updateBlock(props.block, { type: "paragraph" }); + editor.updateBlock(sideMenu.store.state!.block, { type: "paragraph" }); }} > - Reset Type + {props.children} ); } diff --git a/examples/03-ui-components/06-suggestion-menus-slash-menu-items/src/App.tsx b/examples/03-ui-components/06-suggestion-menus-slash-menu-items/src/App.tsx index 61fc407ff7..c5f65cfdd8 100644 --- a/examples/03-ui-components/06-suggestion-menus-slash-menu-items/src/App.tsx +++ b/examples/03-ui-components/06-suggestion-menus-slash-menu-items/src/App.tsx @@ -1,8 +1,8 @@ +import { BlockNoteEditor } from "@blocknote/core"; import { - BlockNoteEditor, filterSuggestionItems, - insertOrUpdateBlock, -} from "@blocknote/core"; + insertOrUpdateBlockForSlashMenu, +} from "@blocknote/core/extensions"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; @@ -22,7 +22,7 @@ const insertHelloWorldItem = (editor: BlockNoteEditor) => ({ // changes its type to the provided block. Otherwise, it inserts the new // block below and moves the text caret to it. We use this function with // a block containing 'Hello World' in bold. - insertOrUpdateBlock(editor, { + insertOrUpdateBlockForSlashMenu(editor, { type: "paragraph", content: [{ type: "text", text: "Hello World", styles: { bold: true } }], }), diff --git a/examples/03-ui-components/10-suggestion-menus-grid-mentions/src/App.tsx b/examples/03-ui-components/10-suggestion-menus-grid-mentions/src/App.tsx index 333c645135..0c2f9f6c21 100644 --- a/examples/03-ui-components/10-suggestion-menus-grid-mentions/src/App.tsx +++ b/examples/03-ui-components/10-suggestion-menus-grid-mentions/src/App.tsx @@ -1,8 +1,8 @@ import { BlockNoteSchema, defaultInlineContentSpecs, - filterSuggestionItems, } from "@blocknote/core"; +import { filterSuggestionItems } from "@blocknote/core/extensions"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; diff --git a/examples/03-ui-components/11-uppy-file-panel/src/FileReplaceButton.tsx b/examples/03-ui-components/11-uppy-file-panel/src/FileReplaceButton.tsx index e73723a9f9..6fccc130e4 100644 --- a/examples/03-ui-components/11-uppy-file-panel/src/FileReplaceButton.tsx +++ b/examples/03-ui-components/11-uppy-file-panel/src/FileReplaceButton.tsx @@ -70,7 +70,7 @@ export const FileReplaceButton = () => { variant={"panel-popover"} > {/* Replaces default file panel with our Uppy one. */} - + ); diff --git a/examples/03-ui-components/11-uppy-file-panel/src/UppyFilePanel.tsx b/examples/03-ui-components/11-uppy-file-panel/src/UppyFilePanel.tsx index eaf2d4c253..4094bc4441 100644 --- a/examples/03-ui-components/11-uppy-file-panel/src/UppyFilePanel.tsx +++ b/examples/03-ui-components/11-uppy-file-panel/src/UppyFilePanel.tsx @@ -43,7 +43,7 @@ const uppy = new Uppy() }); export function UppyFilePanel(props: FilePanelProps) { - const { block } = props; + const { blockId } = props; const editor = useBlockNoteEditor(); useEffect(() => { @@ -68,7 +68,7 @@ export function UppyFilePanel(props: FilePanelProps) { url: response.uploadURL, }, }; - editor.updateBlock(block, updateData); + editor.updateBlock(blockId, updateData); // File should be removed from the Uppy instance after upload. uppy.removeFile(file.id); @@ -78,7 +78,7 @@ export function UppyFilePanel(props: FilePanelProps) { return () => { uppy.off("upload-success", handler); }; - }, [block, editor]); + }, [blockId, editor]); // set up dashboard as in https://uppy.io/examples/ return ; diff --git a/examples/03-ui-components/13-custom-ui/src/App.tsx b/examples/03-ui-components/13-custom-ui/src/App.tsx index 4fe6427ded..3a351264cd 100644 --- a/examples/03-ui-components/13-custom-ui/src/App.tsx +++ b/examples/03-ui-components/13-custom-ui/src/App.tsx @@ -1,4 +1,5 @@ -import { filterSuggestionItems } from "@blocknote/core"; +import { } from "@blocknote/core"; +import { filterSuggestionItems } from "@blocknote/core/extensions"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteViewRaw, diff --git a/examples/03-ui-components/13-custom-ui/src/MUISideMenu.tsx b/examples/03-ui-components/13-custom-ui/src/MUISideMenu.tsx index 8f42580c1f..e0b25b1b23 100644 --- a/examples/03-ui-components/13-custom-ui/src/MUISideMenu.tsx +++ b/examples/03-ui-components/13-custom-ui/src/MUISideMenu.tsx @@ -1,4 +1,11 @@ -import { SideMenuProps } from "@blocknote/react"; +import {} from "@blocknote/core"; +import { SideMenu } from "@blocknote/core/extensions"; +import { + SideMenuProps, + useBlockNoteEditor, + useExtension, + useExtensionState, +} from "@blocknote/react"; import { Delete, DragIndicator } from "@mui/icons-material"; import { Box, @@ -9,22 +16,22 @@ import { MenuItem, Typography, } from "@mui/material"; -import { MouseEvent, ReactNode, useCallback, useMemo, useState } from "react"; - -import { TextBlockSchema } from "./schema"; +import { MouseEvent, ReactNode, useCallback, useState } from "react"; // This replaces the default `RemoveBlockItem` component with a simplified // MUI version: // https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/SideMenu/DragHandleMenu/DefaultItems/RemoveBlockItem.tsx function MUIRemoveBlockItem( - props: SideMenuProps & { closeDragHandleMenu: () => void }, + props: SideMenuProps & { closeDragHandleMenu: () => void }, ) { + const editor = useBlockNoteEditor(); + const sideMenu = useExtension(SideMenu, { editor }); // Deletes the block next to the side menu. const onClick = useCallback(() => { - props.unfreezeMenu(); + sideMenu.unfreezeMenu(); props.closeDragHandleMenu(); - props.editor.removeBlocks([props.editor.getTextCursorPosition().block]); - props.editor.focus(); + editor.removeBlocks([editor.getTextCursorPosition().block]); + editor.focus(); }, [props]); return ( @@ -70,17 +77,19 @@ function MUIDragHandleMenu(props: { // This replaces the default `DragHandleButton` component with a simplified MUI // version: // https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/SideMenu/DefaultButtons/DragHandleButton.tsx -function MUIDragHandleButton(props: SideMenuProps) { +function MUIDragHandleButton(props: SideMenuProps) { // Anchor/trigger element for the color menu. const [anchorEl, setAnchorEl] = useState(null); + const editor = useBlockNoteEditor(); + const sideMenu = useExtension(SideMenu, { editor }); // Handles opening and closing the drag handle menu. const onClick = useCallback( (event: MouseEvent) => { - props.freezeMenu(); + sideMenu.freezeMenu(); setAnchorEl(event.currentTarget); }, - [props], + [sideMenu], ); const onClose = useCallback(() => { setAnchorEl(null); @@ -93,8 +102,10 @@ function MUIDragHandleButton(props: SideMenuProps) { component={"button"} draggable={"true"} onClick={onClick} - onDragStart={(e) => props.blockDragStart(e, props.block)} - onDragEnd={props.blockDragEnd} + onDragStart={(e) => + sideMenu.blockDragStart(e, sideMenu.store.state!.block) + } + onDragEnd={sideMenu.blockDragEnd} > ) { // This replaces the generic Mantine `SideMenu` component: // https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/sideMenu/SideMenu.tsx -function MUISideMenu( - props: SideMenuProps & { children: ReactNode }, -) { +function MUISideMenu(props: SideMenuProps & { children: ReactNode }) { // Since the side menu is positioned by the top-left corner of a block, we // manually set its height based on the hovered block so that it's vertically // centered. - const sideMenuHeight = useMemo(() => { - if (props.block.type === "heading") { - if (props.block.props.level === 1) { - return 78; - } + const sideMenuHeight = useExtensionState(SideMenu, { + selector: (state) => { + // TODO this feels like a hack + if (state && state.block.type === "heading") { + if (state.block.props.level === 1) { + return 78; + } - if (props.block.props.level === 2) { - return 54; - } + if (state.block.props.level === 2) { + return 54; + } - if (props.block.props.level === 3) { - return 37; + if (state.block.props.level === 3) { + return 37; + } } - } - return 30; - }, [props.block]); + return 30; + }, + }); return ( ) { +export function CustomMUISideMenu(props: SideMenuProps) { return ( diff --git a/examples/03-ui-components/16-link-toolbar-buttons/src/App.tsx b/examples/03-ui-components/16-link-toolbar-buttons/src/App.tsx index 744a1e2bd1..6f28b0458c 100644 --- a/examples/03-ui-components/16-link-toolbar-buttons/src/App.tsx +++ b/examples/03-ui-components/16-link-toolbar-buttons/src/App.tsx @@ -55,10 +55,10 @@ export default function App() { - + {/* Extra button to open alert. */} diff --git a/examples/05-interoperability/05-converting-blocks-to-pdf/src/App.tsx b/examples/05-interoperability/05-converting-blocks-to-pdf/src/App.tsx index d910886113..7a34932ec8 100644 --- a/examples/05-interoperability/05-converting-blocks-to-pdf/src/App.tsx +++ b/examples/05-interoperability/05-converting-blocks-to-pdf/src/App.tsx @@ -1,9 +1,9 @@ import { BlockNoteSchema, combineByGroup, - filterSuggestionItems, withPageBreak, } from "@blocknote/core"; +import { filterSuggestionItems } from "@blocknote/core/extensions"; import "@blocknote/core/fonts/inter.css"; import * as locales from "@blocknote/core/locales"; import { BlockNoteView } from "@blocknote/mantine"; diff --git a/examples/05-interoperability/06-converting-blocks-to-docx/src/App.tsx b/examples/05-interoperability/06-converting-blocks-to-docx/src/App.tsx index 9c91a99aff..8c5ae2398f 100644 --- a/examples/05-interoperability/06-converting-blocks-to-docx/src/App.tsx +++ b/examples/05-interoperability/06-converting-blocks-to-docx/src/App.tsx @@ -1,9 +1,9 @@ import { BlockNoteSchema, combineByGroup, - filterSuggestionItems, withPageBreak, } from "@blocknote/core"; +import { filterSuggestionItems } from "@blocknote/core/extensions"; import "@blocknote/core/fonts/inter.css"; import * as locales from "@blocknote/core/locales"; import { BlockNoteView } from "@blocknote/mantine"; diff --git a/examples/05-interoperability/07-converting-blocks-to-odt/src/App.tsx b/examples/05-interoperability/07-converting-blocks-to-odt/src/App.tsx index 9ece22d905..fcfa07ff85 100644 --- a/examples/05-interoperability/07-converting-blocks-to-odt/src/App.tsx +++ b/examples/05-interoperability/07-converting-blocks-to-odt/src/App.tsx @@ -2,8 +2,8 @@ import { BlockNoteSchema, combineByGroup, withPageBreak, - filterSuggestionItems, } from "@blocknote/core"; +import { filterSuggestionItems } from "@blocknote/core/extensions"; import * as locales from "@blocknote/core/locales"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; diff --git a/examples/05-interoperability/08-converting-blocks-to-react-email/src/App.tsx b/examples/05-interoperability/08-converting-blocks-to-react-email/src/App.tsx index 9b46d8e927..5d3d896b8f 100644 --- a/examples/05-interoperability/08-converting-blocks-to-react-email/src/App.tsx +++ b/examples/05-interoperability/08-converting-blocks-to-react-email/src/App.tsx @@ -3,9 +3,9 @@ import { COLORS_DARK_MODE_DEFAULT, COLORS_DEFAULT, combineByGroup, - filterSuggestionItems, withPageBreak, } from "@blocknote/core"; +import { filterSuggestionItems } from "@blocknote/core/extensions"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; diff --git a/examples/06-custom-schema/02-suggestion-menus-mentions/src/App.tsx b/examples/06-custom-schema/02-suggestion-menus-mentions/src/App.tsx index 82bbccdf59..4339153441 100644 --- a/examples/06-custom-schema/02-suggestion-menus-mentions/src/App.tsx +++ b/examples/06-custom-schema/02-suggestion-menus-mentions/src/App.tsx @@ -1,8 +1,8 @@ import { BlockNoteSchema, defaultInlineContentSpecs, - filterSuggestionItems, } from "@blocknote/core"; +import { filterSuggestionItems } from "@blocknote/core/extensions"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; diff --git a/examples/06-custom-schema/04-pdf-file-block/src/App.tsx b/examples/06-custom-schema/04-pdf-file-block/src/App.tsx index f0252e4dc4..3c244a2840 100644 --- a/examples/06-custom-schema/04-pdf-file-block/src/App.tsx +++ b/examples/06-custom-schema/04-pdf-file-block/src/App.tsx @@ -1,9 +1,8 @@ +import { BlockNoteSchema, defaultBlockSpecs } from "@blocknote/core"; import { - BlockNoteSchema, - defaultBlockSpecs, filterSuggestionItems, - insertOrUpdateBlock, -} from "@blocknote/core"; + insertOrUpdateBlockForSlashMenu, +} from "@blocknote/core/extensions"; import { en } from "@blocknote/core/locales"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; @@ -39,7 +38,7 @@ const schema = BlockNoteSchema.create({ const insertPDF = (editor: typeof schema.BlockNoteEditor) => ({ title: "PDF", onItemClick: () => { - insertOrUpdateBlock(editor, { + insertOrUpdateBlockForSlashMenu(editor, { type: "pdf", }); }, diff --git a/examples/06-custom-schema/05-alert-block-full-ux/src/App.tsx b/examples/06-custom-schema/05-alert-block-full-ux/src/App.tsx index 21dc29650a..625dcce896 100644 --- a/examples/06-custom-schema/05-alert-block-full-ux/src/App.tsx +++ b/examples/06-custom-schema/05-alert-block-full-ux/src/App.tsx @@ -1,9 +1,8 @@ +import { BlockNoteSchema, defaultBlockSpecs } from "@blocknote/core"; import { - BlockNoteSchema, - defaultBlockSpecs, filterSuggestionItems, - insertOrUpdateBlock, -} from "@blocknote/core"; + insertOrUpdateBlockForSlashMenu, +} from "@blocknote/core/extensions"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; @@ -38,7 +37,7 @@ const insertAlert = (editor: typeof schema.BlockNoteEditor) => ({ // changes its type to the provided block. Otherwise, it inserts the new // block below and moves the text caret to it. We use this function with an // Alert block. - insertOrUpdateBlock(editor, { + insertOrUpdateBlockForSlashMenu(editor, { type: "alert", }), aliases: [ diff --git a/examples/07-collaboration/08-forking/src/App.tsx b/examples/07-collaboration/08-forking/src/App.tsx index ba6398bb51..134935d546 100644 --- a/examples/07-collaboration/08-forking/src/App.tsx +++ b/examples/07-collaboration/08-forking/src/App.tsx @@ -1,11 +1,15 @@ import "@blocknote/core/fonts/inter.css"; -import { useCreateBlockNote } from "@blocknote/react"; +import {} from "@blocknote/core"; +import { ForkYDoc } from "@blocknote/core/extensions"; +import { + useCreateBlockNote, + useExtension, + useExtensionState, +} from "@blocknote/react"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; import YPartyKitProvider from "y-partykit/provider"; import * as Y from "yjs"; -import { useEffect } from "react"; -import { useState } from "react"; // Sets up Yjs document and PartyKit Yjs provider. const doc = new Y.Doc(); @@ -30,18 +34,18 @@ export default function App() { }, }, }); - const [isForked, setIsForked] = useState(false); - - useEffect(() => { - editor.forkYDocPlugin!.on("forked", setIsForked); - }, [editor]); + const forkYDocPlugin = useExtension(ForkYDoc, { editor }); + const isForked = useExtensionState(ForkYDoc, { + editor, + selector: (state) => state.isForked, + }); // Renders the editor instance. return ( <>