UI With Third-Party Components
In this example, we implement a basic editor interface using components from Material UI. We replace the Formatting Toolbar, Slash Menu, and Block Side Menu while disabling the other default elements. Additionally, the Formatting Toolbar is made static and always visible above the editor.
Relevant Docs:
import { filterSuggestionItems } from "@blocknote/core/extensions";import "@blocknote/core/fonts/inter.css";import { BlockNoteViewRaw, getDefaultReactSlashMenuItems, SideMenuController, SuggestionMenuController, useCreateBlockNote,} from "@blocknote/react";import "@blocknote/react/style.css";import { createTheme, ThemeProvider, useMediaQuery } from "@mui/material";import { useMemo } from "react";import { schema } from "./schema";import { CustomMUIFormattingToolbar } from "./MUIFormattingToolbar";import { CustomMUISideMenu } from "./MUISideMenu";import { MUISuggestionMenu } from "./MUISuggestionMenu";import "./style.css";export default function App() { // Creates a new editor instance. const editor = useCreateBlockNote({ schema, initialContent: [ { type: "paragraph", content: "Welcome to this demo!", }, { type: "paragraph", }, ], }); // Automatically sets light/dark mode based on the user's system preferences. const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)"); const theme = useMemo( () => createTheme({ palette: { mode: prefersDarkMode ? "dark" : "light", }, }), [prefersDarkMode], ); // Renders the editor instance. return ( // Provides theming for Material UI. <ThemeProvider theme={theme}> <BlockNoteViewRaw editor={editor} // Disables the UI components we'll be replacing. formattingToolbar={false} slashMenu={false} sideMenu={false} // Disables remaining UI elements. linkToolbar={false} filePanel={false} tableHandles={false} emojiPicker={false} > {/* Adds the custom Formatting Toolbar. */} {/* `FormattingToolbarController isn't used since we make the custom */} {/* toolbar static and always visible above the editor for this */} {/* example. */} <CustomMUIFormattingToolbar /> {/* Adds the custom Side Menu and Slash Menu. */} {/* These use controllers since we want them to be positioned and */} {/* show/hide the same as the default ones. */} <SideMenuController sideMenu={CustomMUISideMenu} /> <SuggestionMenuController triggerCharacter={"/"} // We use the default slash menu items but exclude the `Emoji` one as // we disabled the Emoji Picker. getItems={async (query) => filterSuggestionItems( getDefaultReactSlashMenuItems(editor).filter( (item) => item.title !== "Emoji", ), query, ) } suggestionMenuComponent={MUISuggestionMenu} onItemClick={(i) => i.onItemClick()} /> </BlockNoteViewRaw> </ThemeProvider> );}import { Block } from "@blocknote/core";import { blockTypeSelectItems, useBlockNoteEditor, useEditorState,} from "@blocknote/react";import { Done, FormatAlignCenter, FormatAlignLeft, FormatAlignRight, FormatBold, FormatColorText, FormatItalic, FormatStrikethrough, FormatUnderlined,} from "@mui/icons-material";import { AppBar, Box, Button, ButtonGroup, Container, Divider, FormControl, ListItemIcon, ListItemText, Menu, MenuItem, Select, SelectChangeEvent, Toolbar, Tooltip, Typography,} from "@mui/material";import { MouseEvent, useCallback, useState, useMemo, FC, ReactNode,} from "react";import { TextBlockSchema } from "./schema";// This replaces the generic Mantine `ToolbarSelect` component with a simplified// MUI version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/toolbar/ToolbarSelect.tsx// In this example, we use it to create a replacement for the default Formatting// Toolbar select element (i.e. the Block Type Select) using MUI, but you can// also use it to add custom select elements.function MUIToolbarSelect<Item extends { name: string; icon?: FC }>(props: { items: Item[]; selectedItem: Item; onChange: (event: SelectChangeEvent<string>) => void;}) { return ( <FormControl size={"small"} fullWidth sx={{ "& .MuiSelect-select": { display: "flex", alignItems: "center", paddingRight: "1em", }, "& .MuiSelect-select, & .MuiOutlinedInput-notchedOutline, & .MuiSvgIcon-root": { color: (theme) => `${ theme.palette.mode === "dark" ? theme.palette.primary.main : theme.palette.background.default } !important`, borderColor: (theme) => `${ theme.palette.mode === "dark" ? theme.palette.primary.main : theme.palette.background.default } !important`, }, }} > <Select value={props.selectedItem.name} onChange={props.onChange}> {props.items.map((item) => ( <MenuItem key={item.name} value={item.name}> <Box sx={{ display: "flex", alignItems: "center", paddingRight: "1em", }} > {item.icon && <item.icon />} </Box> <Box>{item.name}</Box> </MenuItem> ))} </Select> </FormControl> );}// This replaces the default `BlockTypeSelect` component with a simplified MUI// version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultSelects/BlockTypeSelect.tsxfunction MUIBlockTypeSelect() { const editor = useBlockNoteEditor<TextBlockSchema>(); // The block currently containing the text cursor. const block = useEditorState({ editor, selector: ({ editor }) => editor.getTextCursorPosition().block, }); // Gets the default items for the select. const defaultBlockTypeSelectItems = useMemo( () => blockTypeSelectItems(editor.dictionary), [editor.dictionary], ); // Gets the selected item. const selectedItem = useMemo( () => defaultBlockTypeSelectItems.find((item) => { const typesMatch = item.type === block.type; const propsMatch = Object.entries(item.props || {}).filter( ([propName, propValue]) => propValue !== (block as any).props[propName], ).length === 0; return typesMatch && propsMatch; })!, [defaultBlockTypeSelectItems, block], ); // Updates the state when the user chooses an item. const onChange = useCallback( (event: SelectChangeEvent<string>) => { const newSelectedItem = defaultBlockTypeSelectItems.find( (item) => item.name === event.target.value, )!; editor.updateBlock(block, { type: newSelectedItem.type as keyof TextBlockSchema, props: newSelectedItem.props, }); editor.focus(); }, [block, defaultBlockTypeSelectItems, editor], ); return ( <MUIToolbarSelect items={defaultBlockTypeSelectItems} selectedItem={selectedItem} onChange={onChange} /> );}// This replaces the generic Mantine `ToolbarButton` component with a simplified// MUI version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/toolbar/ToolbarButton.tsx// In this example, we use it to create replacements for the default Formatting// Toolbar buttons using MUI, but you can also use it to add custom buttons.function MUIToolbarButton(props: { tooltip: string; selected?: boolean; onClick: (event: MouseEvent<HTMLButtonElement>) => void; children: ReactNode;}) { return ( <Tooltip title={props.tooltip} arrow> <Button size={"small"} variant={props.selected ? "contained" : "text"} onClick={props.onClick} sx={{ my: 2, color: (theme) => !props.selected ? theme.palette.mode === "dark" ? theme.palette.primary.main : theme.palette.background.default : undefined, display: "block", }} > {props.children} </Button> </Tooltip> );}const basicTextStyleIcons = { bold: FormatBold, italic: FormatItalic, underline: FormatUnderlined, strike: FormatStrikethrough,};// This replaces the default `BasicTextStyleButton` component with a simplified// MUI version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/BasicTextStyleButton.tsxfunction MUIBasicTextStyleButton(props: { textStyle: "bold" | "italic" | "underline" | "strike";}) { const Icon = basicTextStyleIcons[props.textStyle]; const editor = useBlockNoteEditor<TextBlockSchema>(); // Whether the text style is currently active. const textStyleActive = useEditorState({ editor, selector: ({ editor }) => props.textStyle in editor.getActiveStyles(), }); // Tooltip for the button. const tooltip = useMemo( () => `Toggle ${props.textStyle .slice(0, 1) .toUpperCase()}${props.textStyle.slice(1)}`, [props.textStyle], ); // Toggles the text style when the button is clicked. const onClick = useCallback(() => { editor.toggleStyles({ [props.textStyle]: true }); editor.focus(); }, [editor, props.textStyle]); return ( <MUIToolbarButton tooltip={tooltip} selected={textStyleActive} onClick={onClick} > <Icon sx={{ padding: "0.1em", height: "100%", width: "0.8em" }} /> </MUIToolbarButton> );}const textAlignIcons = { left: FormatAlignLeft, center: FormatAlignCenter, right: FormatAlignRight,};// This replaces the default `TextAlignButton` component with a simplified MUI// version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/TextAlignButton.tsxfunction MUITextAlignButton(props: { textAlignment: "left" | "center" | "right";}) { const Icon = textAlignIcons[props.textAlignment]; const editor = useBlockNoteEditor<TextBlockSchema>(); // The text alignment of the block currently containing the text cursor. const activeTextAlignment = useEditorState({ editor, selector: ({ editor }) => { const props = editor.getTextCursorPosition().block.props; if ("textAlignment" in props) { return props.textAlignment; } return undefined; }, }); // Tooltip for the button. const tooltip = useMemo( () => `Align ${props.textAlignment .slice(0, 1) .toUpperCase()}${props.textAlignment.slice(1)}`, [props.textAlignment], ); // Sets the text alignment of the block currently containing the text cursor // when the button is clicked. const onClick = useCallback(() => { editor.updateBlock(editor.getTextCursorPosition().block, { props: { textAlignment: props.textAlignment }, }); editor.focus(); }, [editor, props.textAlignment]); if (!activeTextAlignment) { return null; } return ( <MUIToolbarButton tooltip={tooltip} selected={activeTextAlignment === props.textAlignment} onClick={onClick} > <Icon sx={{ padding: "0.1em", height: "100%", width: "0.8em" }} /> </MUIToolbarButton> );}// The highlight colors used by BlockNote.const colors = [ "default", "red", "orange", "yellow", "green", "blue", "purple",] as const;// This replaces the default `ColorStyleButton` component with a simplified MUI// version. The original component can be found here:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/ColorStyleButton.tsxfunction MUIColorStyleButton() { const editor = useBlockNoteEditor<TextBlockSchema>(); // Anchor/trigger element for the color menu. const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); // The active text and background colors. const activeTextColor = useEditorState({ editor, selector: ({ editor }) => editor.getActiveStyles().textColor || "default", }); const activeBackgroundColor = useEditorState({ editor, selector: ({ editor }) => editor.getActiveStyles().backgroundColor || "default", }); // Handles opening and closing the color menu. const onClick = useCallback( (event: MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget), [], ); const onClose = useCallback(() => setAnchorEl(null), []); // Set the text or background color and close the color menu when a color is // clicked. const textColorOnClick = useCallback( (textColor: string) => { setAnchorEl(null); textColor === "default" ? editor.removeStyles({ textColor }) : editor.addStyles({ textColor }); setTimeout(() => editor.focus()); }, [editor], ); const backgroundColorOnClick = useCallback( (backgroundColor: string) => { setAnchorEl(null); backgroundColor === "default" ? editor.removeStyles({ backgroundColor }) : editor.addStyles({ backgroundColor }); setTimeout(() => editor.focus()); }, [editor], ); return ( <> <MUIToolbarButton tooltip={"Text & Background Color"} selected={anchorEl !== null} onClick={onClick} > <FormatColorText sx={{ padding: "0.1em", height: "100%", width: "0.8em" }} /> </MUIToolbarButton> <Menu open={anchorEl !== null} container={document.querySelector(".bn-container")!} anchorEl={anchorEl} onClose={onClose} > <MenuItem disabled> <Typography variant={"body2"}>Text Color</Typography> </MenuItem> {colors.map((color) => ( <MenuItem key={color} onClick={() => textColorOnClick(color)}> <ListItemIcon> <FormatColorText className={"text-" + color} sx={{ padding: "0.1em", height: "0.8em", width: "0.8em" }} /> </ListItemIcon> <ListItemText> <Typography variant={"body2"}> {color.slice(0, 1).toUpperCase() + color.slice(1)} </Typography> </ListItemText> {color === activeTextColor && ( <Done sx={{ padding: "0.1em", height: "0.8em", width: "0.8em" }} /> )} </MenuItem> ))} <Divider /> <MenuItem disabled> <Typography variant={"body2"}>Background Color</Typography> </MenuItem> {colors.map((color) => ( <MenuItem key={color} onClick={() => backgroundColorOnClick(color)}> <ListItemIcon> <FormatColorText className={"background-" + color} sx={{ padding: "0.1em", height: "0.8em", width: "0.8em" }} /> </ListItemIcon> <ListItemText> <Typography variant={"body2"}> {color.slice(0, 1).toUpperCase() + color.slice(1)} </Typography> </ListItemText> {color === activeBackgroundColor && ( <Done sx={{ padding: "0.1em", height: "0.8em", width: "0.8em" }} /> )} </MenuItem> ))} </Menu> </> );}// This replaces the generic Mantine `Toolbar` component:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/toolbar/ToolbarSelect.tsx// In this example, we use it to create a replacement for the default Formatting// Toolbar using MUI, but you can also use it to replace the default Link// Toolbar.function MUIToolbar(props: { children?: ReactNode }) { return ( <AppBar position="static" sx={{ borderRadius: "4px" }}> <Container maxWidth="xl"> <Toolbar disableGutters sx={{ gap: "1em" }}> {props.children} </Toolbar> </Container> </AppBar> );}// This replaces the default `FormattingToolbar` component with a simplified MUI// version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/FormattingToolbar.tsx// You can remove any of the default selects/buttons, or add custom// ones as children of the `MUIToolbar` component here.export function CustomMUIFormattingToolbar() { return ( <MUIToolbar> <MUIBlockTypeSelect /> <ButtonGroup size={"small"} variant={"text"} aria-label="Font style buttons" > {/* Replaces the `BasicTextStyleButton` component: */} <MUIBasicTextStyleButton textStyle={"bold"} /> <MUIBasicTextStyleButton textStyle={"italic"} /> <MUIBasicTextStyleButton textStyle={"underline"} /> <MUIBasicTextStyleButton textStyle={"strike"} /> </ButtonGroup> <ButtonGroup variant="text" aria-label="Text alignment buttons"> {/* Replaces the `TextAlignButton` component: */} <MUITextAlignButton textAlignment={"left"} /> <MUITextAlignButton textAlignment={"center"} /> <MUITextAlignButton textAlignment={"right"} /> </ButtonGroup> <ButtonGroup size={"small"} variant={"text"} aria-label={"Text & background color button"} > <MUIColorStyleButton /> </ButtonGroup> </MUIToolbar> );}import {} from "@blocknote/core";import { SideMenuExtension } from "@blocknote/core/extensions";import { SideMenuProps, useBlockNoteEditor, useExtension, useExtensionState,} from "@blocknote/react";import { Delete, DragIndicator } from "@mui/icons-material";import { Box, IconButton, ListItemIcon, ListItemText, Menu, MenuItem, Typography,} from "@mui/material";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.tsxfunction MUIRemoveBlockItem( props: SideMenuProps & { closeDragHandleMenu: () => void },) { const editor = useBlockNoteEditor(); const sideMenu = useExtension(SideMenuExtension, { editor }); // Deletes the block next to the side menu. const onClick = useCallback(() => { sideMenu.unfreezeMenu(); props.closeDragHandleMenu(); editor.removeBlocks([editor.getTextCursorPosition().block]); editor.focus(); }, [props]); return ( <MenuItem onClick={onClick}> <ListItemIcon> <Delete sx={{ color: (theme) => theme.palette.text.primary, padding: "0.1em", height: "0.8em", width: "0.8em", }} /> </ListItemIcon> <ListItemText> <Typography variant={"body2"}>Delete Block</Typography> </ListItemText> </MenuItem> );}// This replaces the default `DragHandleMenu` component with a simplified MUI// version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/SideMenu/DragHandleMenu/DragHandleMenu.tsxfunction MUIDragHandleMenu(props: { anchorEl: HTMLElement | null; container: Element; onClose: () => void; children: ReactNode;}) { return ( <Menu open={props.anchorEl !== null} container={props.container} anchorEl={props.anchorEl} onClose={props.onClose} > {props.children} </Menu> );}// 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.tsxfunction MUIDragHandleButton(props: SideMenuProps) { // Anchor/trigger element for the color menu. const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null); const editor = useBlockNoteEditor(); const sideMenu = useExtension(SideMenuExtension, { editor }); const block = useExtensionState(SideMenuExtension, { editor, selector: (state) => state?.block, }); // Handles opening and closing the drag handle menu. const onClick = useCallback( (event: MouseEvent<HTMLButtonElement>) => { sideMenu.freezeMenu(); setAnchorEl(event.currentTarget); }, [sideMenu], ); const onClose = useCallback(() => { setAnchorEl(null); }, []); if (!block) { return null; } return ( <> <IconButton size={"small"} component={"button"} draggable={"true"} onClick={onClick} onDragStart={(e) => sideMenu.blockDragStart(e, block)} onDragEnd={sideMenu.blockDragEnd} > <DragIndicator sx={{ color: (theme) => theme.palette.text.primary, }} /> </IconButton> <MUIDragHandleMenu container={document.querySelector(".bn-container")!} anchorEl={anchorEl} onClose={onClose} > <MUIRemoveBlockItem {...props} closeDragHandleMenu={onClose} /> </MUIDragHandleMenu> </> );}// This replaces the generic Mantine `SideMenu` component:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/sideMenu/SideMenu.tsxfunction 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 = useExtensionState(SideMenuExtension, { selector: (state) => { if (state && state.block.type === "heading") { if (state.block.props.level === 1) { return 78; } if (state.block.props.level === 2) { return 54; } if (state.block.props.level === 3) { return 37; } } return 30; }, }); return ( <Box sx={{ height: `${sideMenuHeight}px`, display: "flex", alignItems: "center", justifyContent: "center", }} > {props.children} </Box> );}// This replaces the default `SideMenu` component with a simplified MUI version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/SideMenu/SideMenu.tsx// You can add to or replace the `MUIDragHandleButton` component using the MUI// `Button` components. Unlike the Formatting Toolbar, we don't use button// components specific to the Side Menu since there is really nothing more to// them than just an MUI `IconButton` and some styles passed via the `sx` prop,// as you can see in `MUIDragHandleButton`.export function CustomMUISideMenu(props: SideMenuProps) { return ( <MUISideMenu {...props}> <MUIDragHandleButton {...props} /> </MUISideMenu> );}import { DefaultReactSuggestionItem, elementOverflow, SuggestionMenuProps, useBlockNoteEditor,} from "@blocknote/react";import { Chip, List, ListItem, ListItemButton, ListItemIcon, ListItemText, ListSubheader, Paper,} from "@mui/material";import { useEffect, useMemo, useRef } from "react";import { TextBlockSchema } from "./schema";// If you want to change the items in a Suggestion Menu, like the Slash Menu,// you don't need to modify any of the components in this file. Instead, you// should change the array returned in the getItems` prop of the// `SuggestionMenuController` in `App.tsx`. The components in this file are only// responsible for rendering a Suggestion Menu, not setting its content.// This replaces the generic Mantine `SuggestionMenuItem` component with a// simplified MUI version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/suggestionMenu/SuggestionMenuItem.tsxfunction MUISuggestionMenuItem( props: Omit<SuggestionMenuProps<DefaultReactSuggestionItem>, "items"> & { item: DefaultReactSuggestionItem & { index: number }; },) { const Icon = props.item.icon; const editor = useBlockNoteEditor<TextBlockSchema>(); // Scrolls to the item if it's detected to overflow the Slash Menu. const itemRef = useRef<HTMLDivElement>(null); useEffect(() => { if (!itemRef.current || props.item.index !== props.selectedIndex) { return; } const overflow = elementOverflow( itemRef.current, itemRef.current.closest( `.MuiPaper-root:has([aria-label="suggestion-menu"])`, )!, ); if (overflow === "top") { itemRef.current.scrollIntoView(true); } else if (overflow === "bottom") { itemRef.current.scrollIntoView(false); } }, [props.item.index, props.selectedIndex]); return ( <ListItem ref={itemRef as any} key={props.item.title} disablePadding sx={{ backgroundColor: (theme) => theme.palette.background.paper, }} > <ListItemButton selected={props.item.index === props.selectedIndex} onClick={() => { props.onItemClick?.(props.item); editor.focus(); }} > <ListItemIcon>{Icon}</ListItemIcon> <ListItemText primary={props.item.title} secondary={props.item.subtext} /> {props.item.badge && <Chip label={props.item.badge} size={"small"} />} </ListItemButton> </ListItem> );}// This replaces the generic Mantine `EmptySuggestionMenuItem` component with a// simplified MUI version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/suggestionMenu/EmptySuggestionMenuItem.tsxfunction MUIEmptySuggestionMenuItem() { return ( <ListItem disablePadding> <ListItemButton> <ListItemText primary={"No items found"} /> </ListItemButton> </ListItem> );}// This replaces the generic Mantine `SuggestionMenuLabel` component with a// simplified MUI version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/suggestionMenu/SuggestionMenuLabel.tsxfunction MUISuggestionMenuLabel(props: { group: string }) { return ( <ListSubheader component="div" id={props.group} sx={{ backgroundColor: (theme) => theme.palette.background.paper, }} > {props.group} </ListSubheader> );}// This replaces the generic Mantine `SuggestionMenu` component with a// simplified MUI version:// https://github.com/TypeCellOS/BlockNote/blob/main/packages/mantine/src/suggestionMenu/SuggestionMenu.tsxexport function MUISuggestionMenu( props: SuggestionMenuProps<DefaultReactSuggestionItem>,) { // Sorts items into their groups. const groups = useMemo(() => { const groups: Record< string, (DefaultReactSuggestionItem & { index: number })[] > = {}; for (let i = 0; i < props.items.length; i++) { const item = props.items[i]; const group = item.group || item.title; if (!groups[group]) { groups[group] = []; } groups[group].push({ ...item, index: i }); } return groups; }, [props.items]); return ( <Paper sx={{ height: "fit-content", maxHeight: "100%", maxWidth: 360, overflow: "auto", }} > <nav aria-label="suggestion-menu"> {props.items.length > 0 ? ( Object.entries(groups).map(([group, items]) => ( <List key={group} component="nav" aria-labelledby="nested-list-subheader" subheader={<MUISuggestionMenuLabel group={group} />} > {items.map((item) => ( <MUISuggestionMenuItem key={item.index} item={item} {...props} /> ))} </List> )) ) : ( <List> <MUIEmptySuggestionMenuItem /> </List> )} </nav> </Paper> );}import { BlockNoteSchema, defaultBlockSpecs } from "@blocknote/core";// Simplified schema without media, file, and table blocks.export const schema = BlockNoteSchema.create({ blockSpecs: { paragraph: defaultBlockSpecs.paragraph, heading: defaultBlockSpecs.heading, bulletListItem: defaultBlockSpecs.bulletListItem, numberedListItem: defaultBlockSpecs.numberedListItem, checkListItem: defaultBlockSpecs.checkListItem, quote: defaultBlockSpecs.quote, },});export type TextBlockSchema = typeof schema.blockSchema;@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");.roboto-thin { font-family: "Roboto", sans-serif; font-weight: 100; font-style: normal;}.roboto-light { font-family: "Roboto", sans-serif; font-weight: 300; font-style: normal;}.roboto-regular { font-family: "Roboto", sans-serif; font-weight: 400; font-style: normal;}.roboto-medium { font-family: "Roboto", sans-serif; font-weight: 500; font-style: normal;}.roboto-bold { font-family: "Roboto", sans-serif; font-weight: 700; font-style: normal;}.roboto-black { font-family: "Roboto", sans-serif; font-weight: 900; font-style: normal;}.roboto-thin-italic { font-family: "Roboto", sans-serif; font-weight: 100; font-style: italic;}.roboto-light-italic { font-family: "Roboto", sans-serif; font-weight: 300; font-style: italic;}.roboto-regular-italic { font-family: "Roboto", sans-serif; font-weight: 400; font-style: italic;}.roboto-medium-italic { font-family: "Roboto", sans-serif; font-weight: 500; font-style: italic;}.roboto-bold-italic { font-family: "Roboto", sans-serif; font-weight: 700; font-style: italic;}.roboto-black-italic { font-family: "Roboto", sans-serif; font-weight: 900; font-style: italic;}.text-default { color: var(--bn-colors-editor-text);}.text-red { color: var(--bn-colors-highlights-red-text);}.text-orange { color: var(--bn-colors-highlights-orange-text);}.text-yellow { color: var(--bn-colors-highlights-yellow-text);}.text-green { color: var(--bn-colors-highlights-green-text);}.text-blue { color: var(--bn-colors-highlights-blue-text);}.text-purple { color: var(--bn-colors-highlights-purple-text);}.background-red { background-color: var(--bn-colors-highlights-red-background);}.background-orange { background-color: var(--bn-colors-highlights-orange-background);}.background-yellow { background-color: var(--bn-colors-highlights-yellow-background);}.background-green { background-color: var(--bn-colors-highlights-green-background);}.background-blue { background-color: var(--bn-colors-highlights-blue-background);}.background-purple { background-color: var(--bn-colors-highlights-purple-background);}/* Positions the formatting toolbar above the editor. */.bn-container { display: flex; flex-direction: column-reverse; gap: 8px;}