Added additional plugins

This commit is contained in:
Nate Kelley 2025-07-29 15:44:10 -06:00
parent 8f74b3bb50
commit d46688bc5b
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
25 changed files with 938 additions and 89 deletions

View File

@ -123,6 +123,7 @@
"font-color-contrast": "^11.1.0",
"framer-motion": "^12.23.11",
"hono": "catalog:",
"html2canvas-pro": "^1.5.11",
"intersection-observer": "^0.12.2",
"js-cookie": "^3.0.5",
"js-yaml": "^4.1.0",
@ -136,6 +137,7 @@
"next": "14.2.30",
"next-themes": "^0.4.6",
"papaparse": "^5.5.3",
"pdf-lib": "^1.17.1",
"platejs": "catalog:",
"pluralize": "^8.0.0",
"posthog-js": "^1.258.2",

View File

@ -0,0 +1,78 @@
import {
PlateContent,
PlateView,
type PlateContentProps,
type PlateViewProps
} from 'platejs/react';
import { cn } from '@/lib/utils';
import { cva, type VariantProps } from 'class-variance-authority';
import React from 'react';
const editorVariants = cva(
cn(
'group/editor',
'relative w-full cursor-text overflow-x-hidden break-words whitespace-pre-wrap select-text',
'rounded-md ring-offset-background focus-visible:outline-none',
'placeholder:text-muted-foreground/80 **:data-slate-placeholder:!top-1/2 **:data-slate-placeholder:-translate-y-1/2 **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!',
'[&_strong]:font-bold'
),
{
defaultVariants: {
variant: 'default'
},
variants: {
disabled: {
true: 'cursor-not-allowed opacity-50'
},
focused: {
true: 'ring-2 ring-ring ring-offset-2'
},
variant: {
ai: 'w-full px-0 text-base md:text-sm',
aiChat:
'max-h-[min(70vh,320px)] w-full max-w-[700px] overflow-y-auto px-3 py-2 text-base md:text-sm',
comment: cn('rounded-none border-none bg-transparent text-sm'),
default: 'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
demo: 'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
fullWidth: 'size-full px-16 pt-4 pb-72 text-base sm:px-24',
none: '',
select: 'px-3 py-2 text-base data-readonly:w-fit'
}
}
}
);
export type EditorProps = PlateContentProps & VariantProps<typeof editorVariants>;
export const Editor = React.forwardRef<HTMLDivElement, EditorProps>(
({ className, disabled, focused, variant, ...props }, ref) => {
return (
<PlateContent
ref={ref}
className={cn(
editorVariants({
disabled,
focused,
variant
}),
className
)}
disabled={disabled}
disableDefaultStyles
{...props}
/>
);
}
);
Editor.displayName = 'Editor';
export function EditorView({
className,
variant,
...props
}: PlateViewProps & VariantProps<typeof editorVariants>) {
return <PlateView {...props} className={cn(editorVariants({ variant }), className)} />;
}
EditorView.displayName = 'EditorView';

View File

@ -9,21 +9,34 @@ interface EditorContainerProps {
disabled?: boolean;
}
const editorContainerVariants = cva('relative cursor-text h-full pr-16 pl-18 py-8', {
variants: {
variant: {
default: 'bg-background',
comment: 'bg-background/50'
const editorContainerVariants = cva(
'relative w-full cursor-text overflow-y-auto bg-background caret-primary select-text selection:bg-brand/25 focus-visible:outline-none [&_.slate-selection-area]:z-50 [&_.slate-selection-area]:border [&_.slate-selection-area]:border-brand/25 [&_.slate-selection-area]:bg-brand/15',
{
variants: {
variant: {
default: ' h-full',
comment: cn(
'flex flex-wrap justify-between gap-1 px-1 py-0.5 text-sm',
'rounded-md border-[1.5px] border-transparent bg-transparent',
'has-[[data-slate-editor]:focus]:border-brand/50 has-[[data-slate-editor]:focus]:ring-2 has-[[data-slate-editor]:focus]:ring-brand/30',
'has-aria-disabled:border-input has-aria-disabled:bg-muted'
),
select: cn(
'group rounded-md border border-input ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2',
'has-data-readonly:w-fit has-data-readonly:cursor-default has-data-readonly:border-transparent has-data-readonly:focus-within:[box-shadow:none]'
)
},
readonly: {
true: 'cursor-text'
}
},
readonly: {
true: 'cursor-text'
defaultVariants: {
variant: 'default',
readonly: false
}
},
defaultVariants: {
variant: 'default',
readonly: false
}
});
);
export function EditorContainer({
className,
@ -36,7 +49,11 @@ export function EditorContainer({
EditorContainerProps) {
return (
<PlateContainer
className={cn(editorContainerVariants({ variant, readonly }), className)}
className={cn(
'ignore-click-outside/toolbar',
editorContainerVariants({ variant, readonly }),
className
)}
{...props}
/>
);

View File

@ -1,31 +0,0 @@
import { PlateContent } from 'platejs/react';
import { cn } from '@/lib/utils';
import { cva } from 'class-variance-authority';
const editorContentVariants = cva('pb-42', {
variants: {
variant: {
default: 'bg-background',
comment: 'bg-background/50'
}
}
});
export function EditorContent({
style,
placeholder,
disabled,
variant,
...props
}: React.ComponentProps<typeof PlateContent> & { variant?: 'default' | 'comment' }) {
return (
<PlateContent
{...props}
style={style}
placeholder={placeholder}
disabled={disabled}
className={cn(editorContentVariants({ variant }))}
//disableDefaultStyles
/>
);
}

View File

@ -2,7 +2,7 @@ import React, { useImperativeHandle } from 'react';
import type { Value, AnyPluginConfig } from 'platejs';
import { Plate, type TPlateEditor } from 'platejs/react';
import { EditorContainer } from './EditorContainer';
import { EditorContent } from './EditorContent';
import { Editor } from './Editor';
import { useReportEditor } from './useReportEditor';
import { useMemoizedFn } from '@/hooks';
import { ReportElements } from '@buster/server-shared/reports';
@ -52,26 +52,12 @@ export const ReportEditor = React.memo(
return (
<Plate editor={editor} readOnly={readOnly}>
{/*
Toolbar is commented out for now. Uncomment and implement as needed.
<FixedToolbar className="justify-start rounded-t-lg">
<MarkToolbarButton nodeType="bold" tooltip="Bold (⌘+B)">
B
</MarkToolbarButton>
<MarkToolbarButton nodeType="italic" tooltip="Italic (⌘+I)">
I
</MarkToolbarButton>
<MarkToolbarButton nodeType="underline" tooltip="Underline (⌘+U)">
U
</MarkToolbarButton>
</FixedToolbar>
*/}
<EditorContainer
variant={variant}
readonly={readOnly}
disabled={disabled}
className={className}>
<EditorContent style={style} placeholder={placeholder} disabled={disabled} />
<Editor style={style} placeholder={placeholder} disabled={disabled} />
</EditorContainer>
</Plate>
);

View File

@ -3,7 +3,7 @@ import { AIKit } from './plugins/ai-kit';
import { AlignKit } from './plugins/align-kit';
import { AutoformatKit } from './plugins/autoformat-kit';
import { BasicBlocksKit } from './plugins/basic-blocks-kit';
import { BasicMarksKit } from './plugins/basic-markd-kit';
import { BasicMarksKit } from './plugins/basic-marks-kit';
import { BlockMenuKit } from './plugins/block-menu-kit';
import { BlockPlaceholderKit } from './plugins/block-placeholder-kit';
import { BlockSelectionKit } from './plugins/block-selection-kit';
@ -25,8 +25,14 @@ import { SlashKit } from './plugins/slash-kit';
import { TableKit } from './plugins/table-kit';
import { TocKit } from './plugins/toc-kit';
import { ToggleKit } from './plugins/toggle-kit';
import { DndKit } from './plugins/dnd-kit-new';
import { MarkdownPlugin } from '@buster/server-shared/lib/report';
import { DndKit } from './plugins/dnd-kit';
import { MentionKit } from './plugins/mention-kit';
import { DiscussionKit } from './plugins/discussion-kit';
import { CommentKit } from './plugins/comment-kit';
import { SuggestionKit } from './plugins/suggestion-kit';
import { DocxKit } from './plugins/docx-kit';
import { MarkdownKit } from './plugins/markdown-kit';
import { FixedToolbarKit } from './plugins/fixed-toolbar-kit';
export const AllEditorPlugins: AnyPluginConfig[] = [
// Core functionality (must be first)
@ -46,6 +52,7 @@ export const AllEditorPlugins: AnyPluginConfig[] = [
...DateKit,
...ColumnKit,
...LinkKit,
...MentionKit,
// //Marks
...BasicMarksKit,
@ -56,6 +63,11 @@ export const AllEditorPlugins: AnyPluginConfig[] = [
...ListKit,
...LineHeightKit,
//Collaboration
...DiscussionKit,
...CommentKit,
...SuggestionKit,
// Editing
...SlashKit,
...AutoformatKit,
@ -67,8 +79,11 @@ export const AllEditorPlugins: AnyPluginConfig[] = [
TrailingBlockPlugin,
// //Parsers
// //UI
MarkdownPlugin,
...MarkdownKit,
...DocxKit,
//UI
...FloatingToolbarKit,
...BlockPlaceholderKit,
...FloatingToolbarKit
...FixedToolbarKit
];

View File

@ -1,6 +1,6 @@
import { AlignKit } from './plugins/align-kit';
import { BasicBlocksKit } from './plugins/basic-blocks-kit';
import { BasicMarksKit } from './plugins/basic-markd-kit';
import { BasicMarksKit } from './plugins/basic-marks-kit';
import { CalloutKit } from './plugins/callout-kit';
import { CodeBlockKit } from './plugins/code-block-kit';
import { ColumnKit } from './plugins/column-kit';

View File

@ -0,0 +1,93 @@
'use client';
import { type Value, TrailingBlockPlugin } from 'platejs';
import { type TPlateEditor, useEditorRef } from 'platejs/react';
import { AIKit } from './plugins/ai-kit';
import { AlignKit } from './plugins/align-kit';
import { AutoformatKit } from './plugins/autoformat-kit';
import { BasicBlocksKit } from './plugins/basic-blocks-kit';
import { BasicMarksKit } from './plugins/basic-marks-kit';
import { BlockMenuKit } from './plugins/block-menu-kit';
import { BlockPlaceholderKit } from './plugins/block-placeholder-kit';
import { CalloutKit } from './plugins/callout-kit';
import { CodeBlockKit } from './plugins/code-block-kit';
import { ColumnKit } from './plugins/column-kit';
import { CommentKit } from './plugins/comment-kit';
import { CursorOverlayKit } from './plugins/cursor-overlay-kit';
import { DateKit } from './plugins/date-kit';
import { DiscussionKit } from './plugins/discussion-kit';
import { DndKit } from './plugins/dnd-kit';
import { DocxKit } from './plugins/docx-kit';
import { EmojiKit } from './plugins/emoji-kit';
import { ExitBreakKit } from './plugins/exit-break-kit';
import { FixedToolbarKit } from './plugins/fixed-toolbar-kit';
import { FloatingToolbarKit } from './plugins/floating-toolbar-kit';
import { FontKit } from './plugins/font-kit';
import { LineHeightKit } from './plugins/line-height-kit';
import { LinkKit } from './plugins/link-kit';
import { ListKit } from './plugins/list-kit';
import { MarkdownKit } from './plugins/markdown-kit';
import { MathKit } from './plugins/math-kit';
import { MediaKit } from './plugins/media-kit';
import { MentionKit } from './plugins/mention-kit';
import { SlashKit } from './plugins/slash-kit';
import { SuggestionKit } from './plugins/suggestion-kit';
import { TableKit } from './plugins/table-kit';
import { TocKit } from './plugins/toc-kit';
import { ToggleKit } from './plugins/toggle-kit';
export const EditorKit = [
...AIKit,
...BlockMenuKit,
// Elements
...BasicBlocksKit,
...CodeBlockKit,
...TableKit,
...ToggleKit,
...TocKit,
...MediaKit,
...CalloutKit,
...ColumnKit,
...MathKit,
...DateKit,
...LinkKit,
...MentionKit,
// Marks
...BasicMarksKit,
...FontKit,
// Block Style
...ListKit,
...AlignKit,
...LineHeightKit,
// Collaboration
...DiscussionKit,
...CommentKit,
...SuggestionKit,
// Editing
...SlashKit,
...AutoformatKit,
...CursorOverlayKit,
...DndKit,
...EmojiKit,
...ExitBreakKit,
TrailingBlockPlugin,
// Parsers
...DocxKit,
...MarkdownKit,
// UI
...BlockPlaceholderKit,
...FixedToolbarKit,
...FloatingToolbarKit
];
export type MyEditor = TPlateEditor<Value, (typeof EditorKit)[number]>;
export const useEditor = () => useEditorRef<MyEditor>();

View File

@ -27,11 +27,11 @@ import {
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu';
import { cn } from '@/lib/utils';
import { BasicMarksKit } from '../plugins/basic-markd-kit';
import { BasicMarksKit } from '../plugins/basic-marks-kit';
import { type TDiscussion, discussionPlugin } from '../plugins/discussion-kit';
import { EditorContainer } from '../EditorContainer';
import { EditorContent as Editor } from '../EditorContent';
import { EditorContent as Editor } from '../Editor';
export interface TComment {
id: string;

View File

@ -0,0 +1,49 @@
import * as React from 'react';
import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';
import { type PlateStaticProps, PlateStatic } from 'platejs';
import { cn } from '@/lib/utils';
export const editorVariants = cva(
cn(
'group/editor',
'relative w-full cursor-text overflow-x-hidden break-words whitespace-pre-wrap select-text',
'rounded-md ring-offset-background focus-visible:outline-none',
'placeholder:text-muted-foreground/80 **:data-slate-placeholder:top-[auto_!important] **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!',
'[&_strong]:font-bold'
),
{
defaultVariants: {
variant: 'none'
},
variants: {
disabled: {
true: 'cursor-not-allowed opacity-50'
},
focused: {
true: 'ring-2 ring-ring ring-offset-2'
},
variant: {
ai: 'w-full px-0 text-base md:text-sm',
aiChat:
'max-h-[min(70vh,320px)] w-full max-w-[700px] overflow-y-auto px-5 py-3 text-base md:text-sm',
default: 'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
demo: 'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
fullWidth: 'size-full px-16 pt-4 pb-72 text-base sm:px-24',
none: '',
select: 'px-3 py-2 text-base data-readonly:w-fit'
}
}
}
);
export function EditorStatic({
className,
variant,
...props
}: PlateStaticProps & VariantProps<typeof editorVariants>) {
return <PlateStatic className={cn(editorVariants({ variant }), className)} {...props} />;
}

View File

@ -0,0 +1,167 @@
'use client';
import * as React from 'react';
import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
import { MarkdownPlugin } from '@platejs/markdown';
import { ArrowDownFromLine } from '../../icons';
import { createSlateEditor, serializeHtml } from 'platejs';
import { useEditorRef } from 'platejs/react';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu';
import { EditorStatic } from './EditorStatic';
import { ToolbarButton } from './Toolbar';
import { BaseEditorKit } from '../editor-base-kit';
const siteUrl = 'https://platejs.org';
export function ExportToolbarButton(props: DropdownMenuProps) {
const editor = useEditorRef();
const [open, setOpen] = React.useState(false);
const getCanvas = async () => {
const { default: html2canvas } = await import('html2canvas-pro');
const style = document.createElement('style');
document.head.append(style);
const canvas = await html2canvas(editor.api.toDOMNode(editor)!, {
onclone: (document: Document) => {
const editorElement = document.querySelector('[contenteditable="true"]');
if (editorElement) {
Array.from(editorElement.querySelectorAll('*')).forEach((element) => {
const existingStyle = element.getAttribute('style') || '';
element.setAttribute(
'style',
`${existingStyle}; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important`
);
});
}
}
});
style.remove();
return canvas;
};
const downloadFile = async (url: string, filename: string) => {
const response = await fetch(url);
const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = filename;
document.body.append(link);
link.click();
link.remove();
// Clean up the blob URL
window.URL.revokeObjectURL(blobUrl);
};
const exportToPdf = async () => {
const canvas = await getCanvas();
const PDFLib = await import('pdf-lib');
const pdfDoc = await PDFLib.PDFDocument.create();
const page = pdfDoc.addPage([canvas.width, canvas.height]);
const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG'));
const { height, width } = imageEmbed.scale(1);
page.drawImage(imageEmbed, {
height,
width,
x: 0,
y: 0
});
const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });
await downloadFile(pdfBase64, 'plate.pdf');
};
const exportToImage = async () => {
const canvas = await getCanvas();
await downloadFile(canvas.toDataURL('image/png'), 'plate.png');
};
const exportToHtml = async () => {
const editorStatic = createSlateEditor({
plugins: BaseEditorKit,
value: editor.children
});
const editorHtml = await serializeHtml(editorStatic, {
editorComponent: EditorStatic,
props: { style: { padding: '0 calc(50% - 350px)', paddingBottom: '' } }
});
const tailwindCss = `<link rel="stylesheet" href="${siteUrl}/tailwind.css">`;
const katexCss = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.css" integrity="sha384-9PvLvaiSKCPkFKB1ZsEoTjgnJn+O3KvEwtsz37/XrkYft3DTk2gHdYvd9oWgW3tV" crossorigin="anonymous">`;
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=JetBrains+Mono:wght@400..700&display=swap"
rel="stylesheet"
/>
${tailwindCss}
${katexCss}
<style>
:root {
--font-sans: 'Inter', 'Inter Fallback';
--font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback';
}
</style>
</head>
<body>
${editorHtml}
</body>
</html>`;
const url = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
await downloadFile(url, 'plate.html');
};
const exportToMarkdown = async () => {
const md = editor.getApi(MarkdownPlugin).markdown.serialize();
const url = `data:text/markdown;charset=utf-8,${encodeURIComponent(md)}`;
await downloadFile(url, 'plate.md');
};
return (
<DropdownMenu open={open} onOpenChange={setOpen} modal={false} {...props}>
<DropdownMenuTrigger asChild>
<ToolbarButton pressed={open} tooltip="Export" isDropdown>
<div className="size-4">
<ArrowDownFromLine />
</div>
</ToolbarButton>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={exportToHtml}>Export as HTML</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToPdf}>Export as PDF</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToImage}>Export as Image</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToMarkdown}>Export as Markdown</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -2,7 +2,7 @@
import { cn } from '@/lib/utils';
import { Toolbar } from './Toolbar';
import { Toolbar } from '../Toolbar';
export function FixedToolbar({ className, ...props }: React.ComponentProps<typeof Toolbar>) {
return (

View File

@ -0,0 +1,137 @@
'use client';
import * as React from 'react';
import {
TextBold,
Code2,
TextItalic,
TextStrikethrough,
TextUnderline,
WandSparkle
} from '@/components/ui/icons';
import { KEYS } from 'platejs';
import { useEditorReadOnly } from 'platejs/react';
import { AIToolbarButton } from './AIToolbarButton';
import { CommentToolbarButton } from './CommandToolbarButton';
import { InlineEquationToolbarButton } from './EquationToolbarButton';
import { LinkToolbarButton } from './LinkToolbarButton';
import { MarkToolbarButton } from './MarktoolbarButton';
import { MoreToolbarButton } from './MoreToolbarButton';
import { SuggestionToolbarButton } from './SuggestionToolbarButton';
import { ToolbarGroup } from './Toolbar';
import { TurnIntoToolbarButton } from './TurnIntoToolbarButton';
import { UndoToolbarButton, RedoToolbarButton } from './UndoToolbarButton';
export function FixedToolbarButtons() {
const readOnly = useEditorReadOnly();
return (
<div className="flex w-full">
{!readOnly && (
<>
<ToolbarGroup>
<UndoToolbarButton />
<RedoToolbarButton />
</ToolbarGroup>
<ToolbarGroup>
<AIToolbarButton tooltip="AI commands">
<WandSparkle />
</AIToolbarButton>
</ToolbarGroup>
<ToolbarGroup>
<ExportToolbarButton>
<ArrowUpToLine />
</ExportToolbarButton>
<ImportToolbarButton />
</ToolbarGroup>
<ToolbarGroup>
<InsertToolbarButton />
<TurnIntoToolbarButton />
<FontSizeToolbarButton />
</ToolbarGroup>
<ToolbarGroup>
<MarkToolbarButton nodeType={KEYS.bold} tooltip="Bold (⌘+B)">
<BoldIcon />
</MarkToolbarButton>
<MarkToolbarButton nodeType={KEYS.italic} tooltip="Italic (⌘+I)">
<ItalicIcon />
</MarkToolbarButton>
<MarkToolbarButton nodeType={KEYS.underline} tooltip="Underline (⌘+U)">
<UnderlineIcon />
</MarkToolbarButton>
<MarkToolbarButton nodeType={KEYS.strikethrough} tooltip="Strikethrough (⌘+⇧+M)">
<StrikethroughIcon />
</MarkToolbarButton>
<MarkToolbarButton nodeType={KEYS.code} tooltip="Code (⌘+E)">
<Code2Icon />
</MarkToolbarButton>
<FontColorToolbarButton nodeType={KEYS.color} tooltip="Text color">
<BaselineIcon />
</FontColorToolbarButton>
<FontColorToolbarButton nodeType={KEYS.backgroundColor} tooltip="Background color">
<PaintBucketIcon />
</FontColorToolbarButton>
</ToolbarGroup>
<ToolbarGroup>
<AlignToolbarButton />
<NumberedListToolbarButton />
<BulletedListToolbarButton />
<TodoListToolbarButton />
<ToggleToolbarButton />
</ToolbarGroup>
<ToolbarGroup>
<LinkToolbarButton />
<TableToolbarButton />
<EmojiToolbarButton />
</ToolbarGroup>
<ToolbarGroup>
<MediaToolbarButton nodeType={KEYS.img} />
<MediaToolbarButton nodeType={KEYS.video} />
<MediaToolbarButton nodeType={KEYS.audio} />
<MediaToolbarButton nodeType={KEYS.file} />
</ToolbarGroup>
<ToolbarGroup>
<LineHeightToolbarButton />
<OutdentToolbarButton />
<IndentToolbarButton />
</ToolbarGroup>
<ToolbarGroup>
<MoreToolbarButton />
</ToolbarGroup>
</>
)}
<div className="grow" />
<ToolbarGroup>
<MarkToolbarButton nodeType={KEYS.highlight} tooltip="Highlight">
<HighlighterIcon />
</MarkToolbarButton>
<CommentToolbarButton />
</ToolbarGroup>
<ToolbarGroup>
<ModeToolbarButton />
</ToolbarGroup>
</div>
);
}

View File

@ -0,0 +1,186 @@
'use client';
import * as React from 'react';
import type { TComboboxInputElement, TMentionElement } from 'platejs';
import type { PlateElementProps } from 'platejs/react';
import { getMentionOnSelectItem } from '@platejs/mention';
import { IS_APPLE, KEYS } from 'platejs';
import { PlateElement, useFocused, useReadOnly, useSelected } from 'platejs/react';
import { cn } from '@/lib/utils';
import { useMounted } from '@/hooks/useMount';
import {
InlineCombobox,
InlineComboboxContent,
InlineComboboxEmpty,
InlineComboboxGroup,
InlineComboboxInput,
InlineComboboxItem
} from './InlineCombobox';
export function MentionElement(
props: PlateElementProps<TMentionElement> & {
prefix?: string;
}
) {
const element = props.element;
const selected = useSelected();
const focused = useFocused();
const mounted = useMounted();
const readOnly = useReadOnly();
return (
<PlateElement
{...props}
className={cn(
'bg-muted inline-block rounded-md px-1.5 py-0.5 align-baseline text-sm font-medium',
!readOnly && 'cursor-pointer',
selected && focused && 'ring-ring ring-2',
element.children[0][KEYS.bold] === true && 'font-bold',
element.children[0][KEYS.italic] === true && 'italic',
element.children[0][KEYS.underline] === true && 'underline'
)}
attributes={{
...props.attributes,
contentEditable: false,
'data-slate-value': element.value,
draggable: true
}}>
{mounted && IS_APPLE ? (
// Mac OS IME https://github.com/ianstormtaylor/slate/issues/3490
<React.Fragment>
{props.children}
{props.prefix}
{element.value}
</React.Fragment>
) : (
// Others like Android https://github.com/ianstormtaylor/slate/pull/5360
<React.Fragment>
{props.prefix}
{element.value}
{props.children}
</React.Fragment>
)}
</PlateElement>
);
}
const onSelectItem = getMentionOnSelectItem();
export function MentionInputElement(props: PlateElementProps<TComboboxInputElement>) {
const { editor, element } = props;
const [search, setSearch] = React.useState('');
return (
<PlateElement {...props} as="span">
<InlineCombobox
value={search}
element={element}
setValue={setSearch}
showTrigger={false}
trigger="@">
<span className="bg-muted ring-ring inline-block rounded-md px-1.5 py-0.5 align-baseline text-sm focus-within:ring-2">
<InlineComboboxInput />
</span>
<InlineComboboxContent className="my-1.5">
<InlineComboboxEmpty>No results</InlineComboboxEmpty>
<InlineComboboxGroup>
{MENTIONABLES.map((item) => (
<InlineComboboxItem
key={item.key}
value={item.text}
onClick={() => onSelectItem(editor, item, search)}>
{item.text}
</InlineComboboxItem>
))}
</InlineComboboxGroup>
</InlineComboboxContent>
</InlineCombobox>
{props.children}
</PlateElement>
);
}
const MENTIONABLES = [
{ key: '0', text: 'Aayla Secura' },
{ key: '1', text: 'Adi Gallia' },
{
key: '2',
text: 'Admiral Dodd Rancit'
},
{
key: '3',
text: 'Admiral Firmus Piett'
},
{
key: '4',
text: 'Admiral Gial Ackbar'
},
{ key: '5', text: 'Admiral Ozzel' },
{ key: '6', text: 'Admiral Raddus' },
{
key: '7',
text: 'Admiral Terrinald Screed'
},
{ key: '8', text: 'Admiral Trench' },
{
key: '9',
text: 'Admiral U.O. Statura'
},
{ key: '10', text: 'Agen Kolar' },
{ key: '11', text: 'Agent Kallus' },
{
key: '12',
text: 'Aiolin and Morit Astarte'
},
{ key: '13', text: 'Aks Moe' },
{ key: '14', text: 'Almec' },
{ key: '15', text: 'Alton Kastle' },
{ key: '16', text: 'Amee' },
{ key: '17', text: 'AP-5' },
{ key: '18', text: 'Armitage Hux' },
{ key: '19', text: 'Artoo' },
{ key: '20', text: 'Arvel Crynyd' },
{ key: '21', text: 'Asajj Ventress' },
{ key: '22', text: 'Aurra Sing' },
{ key: '23', text: 'AZI-3' },
{ key: '24', text: 'Bala-Tik' },
{ key: '25', text: 'Barada' },
{ key: '26', text: 'Bargwill Tomder' },
{ key: '27', text: 'Baron Papanoida' },
{ key: '28', text: 'Barriss Offee' },
{ key: '29', text: 'Baze Malbus' },
{ key: '30', text: 'Bazine Netal' },
{ key: '31', text: 'BB-8' },
{ key: '32', text: 'BB-9E' },
{ key: '33', text: 'Ben Quadinaros' },
{ key: '34', text: 'Berch Teller' },
{ key: '35', text: 'Beru Lars' },
{ key: '36', text: 'Bib Fortuna' },
{
key: '37',
text: 'Biggs Darklighter'
},
{ key: '38', text: 'Black Krrsantan' },
{ key: '39', text: 'Bo-Katan Kryze' },
{ key: '40', text: 'Boba Fett' },
{ key: '41', text: 'Bobbajo' },
{ key: '42', text: 'Bodhi Rook' },
{ key: '43', text: 'Borvo the Hutt' },
{ key: '44', text: 'Boss Nass' },
{ key: '45', text: 'Bossk' },
{
key: '46',
text: 'Breha Antilles-Organa'
},
{ key: '47', text: 'Bren Derlin' },
{ key: '48', text: 'Brendol Hux' },
{ key: '49', text: 'BT-1' }
];

View File

@ -0,0 +1,40 @@
'use client';
import * as React from 'react';
import { useEditorRef, useEditorSelector } from 'platejs/react';
import { Redo, Undo } from '@/components/ui/icons';
import { ToolbarButton } from './Toolbar';
export function RedoToolbarButton(props: React.ComponentProps<typeof ToolbarButton>) {
const editor = useEditorRef();
const disabled = useEditorSelector((editor) => editor.history.redos.length === 0, []);
return (
<ToolbarButton
{...props}
disabled={disabled}
onClick={() => editor.redo()}
onMouseDown={(e) => e.preventDefault()}
tooltip="Redo">
<Redo />
</ToolbarButton>
);
}
export function UndoToolbarButton(props: React.ComponentProps<typeof ToolbarButton>) {
const editor = useEditorRef();
const disabled = useEditorSelector((editor) => editor.history.undos.length === 0, []);
return (
<ToolbarButton
{...props}
disabled={disabled}
onClick={() => editor.undo()}
onMouseDown={(e) => e.preventDefault()}
tooltip="Undo">
<Undo />
</ToolbarButton>
);
}

View File

@ -0,0 +1,6 @@
'use client';
import { DocxPlugin } from '@platejs/docx';
import { JuicePlugin } from '@platejs/juice';
export const DocxKit = [DocxPlugin, JuicePlugin];

View File

@ -0,0 +1,19 @@
'use client';
import { createPlatePlugin } from 'platejs/react';
import { FixedToolbar } from '../elements/FixedToolbar';
import { FixedToolbarButtons } from '../elements/FixedToolbarButtons';
export const FixedToolbarKit = [
createPlatePlugin({
key: 'fixed-toolbar',
render: {
beforeEditable: () => (
<FixedToolbar>
<FixedToolbarButtons />
</FixedToolbar>
)
}
})
];

View File

@ -0,0 +1,3 @@
import { MarkdownPlugin } from '@platejs/markdown';
export const MarkdownKit = [MarkdownPlugin];

View File

@ -0,0 +1,11 @@
'use client';
import { MentionInputPlugin, MentionPlugin } from '@platejs/mention/react';
import { MentionElement, MentionInputElement } from '../elements/MentionNode';
export const MentionKit = [
MentionPlugin.configure({
options: { triggerPreviousCharPattern: /^$|^[\s"']$/ }
}).withComponent(MentionElement),
MentionInputPlugin.withComponent(MentionInputElement)
];

View File

@ -1,6 +1,6 @@
'use client';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
/**
* Hook that executes a callback when a component mounts.
@ -11,3 +11,11 @@ export const useMount = (callback: () => void): void => {
callback();
}, []); // Empty dependency array means this runs once on mount
};
export const useMounted = () => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
return mounted;
};

View File

@ -1,4 +1,3 @@
import { ReportElementsSchema } from '@buster/server-shared/reports';
import { AutoformatPlugin } from '@platejs/autoformat';
import {
BaseBasicBlocksPlugin,
@ -23,6 +22,7 @@ import {
BaseUnderlinePlugin,
} from '@platejs/basic-nodes';
import { createSlateEditor } from 'platejs';
import { ReportElementsSchema } from '../../reports/report-elements';
import { MarkdownPlugin } from './MarkdownPlugin';
const serverNode = [

View File

@ -301,10 +301,10 @@ importers:
version: 49.2.1(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)
'@platejs/autoformat':
specifier: 'catalog:'
version: 49.0.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 49.0.0(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@platejs/basic-nodes':
specifier: 'catalog:'
version: 49.0.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 49.0.0(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@platejs/basic-styles':
specifier: ^49.0.0
version: 49.0.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -355,7 +355,7 @@ importers:
version: 49.2.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@platejs/markdown':
specifier: 'catalog:'
version: 49.2.1(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)
version: 49.2.1(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)
'@platejs/math':
specifier: ^49.0.0
version: 49.0.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -539,6 +539,9 @@ importers:
hono:
specifier: 'catalog:'
version: 4.8.4
html2canvas-pro:
specifier: ^1.5.11
version: 1.5.11
intersection-observer:
specifier: ^0.12.2
version: 0.12.2
@ -578,6 +581,9 @@ importers:
papaparse:
specifier: ^5.5.3
version: 5.5.3
pdf-lib:
specifier: ^1.17.1
version: 1.17.1
platejs:
specifier: 'catalog:'
version: 49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1))
@ -629,9 +635,6 @@ importers:
remark-gfm:
specifier: 'catalog:'
version: 4.0.1
remark-math:
specifier: ^6.0.0
version: 6.0.0
sass:
specifier: ^1.89.2
version: 1.89.2
@ -1044,13 +1047,13 @@ importers:
version: link:../vitest-config
'@platejs/autoformat':
specifier: 'catalog:'
version: 49.0.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 49.0.0(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@platejs/basic-nodes':
specifier: 'catalog:'
version: 49.0.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 49.0.0(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@platejs/markdown':
specifier: 'catalog:'
version: 49.2.1(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)
version: 49.2.1(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)
platejs:
specifier: 'catalog:'
version: 49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1))
@ -3918,6 +3921,12 @@ packages:
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
engines: {node: '>= 10.0.0'}
'@pdf-lib/standard-fonts@1.0.0':
resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==}
'@pdf-lib/upng@1.0.1':
resolution: {integrity: sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@ -6646,6 +6655,10 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
base64-arraybuffer@1.0.2:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@ -7221,6 +7234,9 @@ packages:
resolution: {integrity: sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==}
engines: {node: '>= 0.10'}
css-line-break@2.1.0:
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
css-loader@6.11.0:
resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==}
engines: {node: '>= 12.13.0'}
@ -8625,6 +8641,10 @@ packages:
webpack:
optional: true
html2canvas-pro@1.5.11:
resolution: {integrity: sha512-W4pEeKLG8+9a54RDOSiEKq7gRXXDzt0ORMaLXX+l6a3urSKbmnkmyzcRDCtgTOzmHLaZTLG2wiTQMJqKLlSh3w==}
engines: {node: '>=16.0.0'}
htmlparser2@10.0.0:
resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==}
@ -10304,6 +10324,9 @@ packages:
resolution: {integrity: sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==}
engines: {node: '>=0.12'}
pdf-lib@1.17.1:
resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==}
performance-now@2.1.0:
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
@ -11718,6 +11741,9 @@ packages:
text-hex@1.0.0:
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
text-segmentation@1.0.3:
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@ -12281,6 +12307,9 @@ packages:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
utrie@1.0.2:
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
uuid@11.1.0:
resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==}
hasBin: true
@ -16465,12 +16494,20 @@ snapshots:
'@parcel/watcher-win32-x64': 2.5.1
optional: true
'@pdf-lib/standard-fonts@1.0.0':
dependencies:
pako: 1.0.11
'@pdf-lib/upng@1.0.1':
dependencies:
pako: 1.0.11
'@pkgjs/parseargs@0.11.0':
optional: true
'@platejs/ai@49.2.1(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)':
dependencies:
'@platejs/markdown': 49.2.1(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)
'@platejs/markdown': 49.2.1(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)
'@platejs/selection': 49.1.12(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
lodash: 4.17.21
platejs: 49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1))
@ -16480,14 +16517,14 @@ snapshots:
- supports-color
- typescript
'@platejs/autoformat@49.0.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
'@platejs/autoformat@49.0.0(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
lodash: 4.17.21
platejs: 49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1))
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@platejs/basic-nodes@49.0.0(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
'@platejs/basic-nodes@49.0.0(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
platejs: 49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1))
react: 18.3.1
@ -16543,7 +16580,7 @@ snapshots:
html-entities: 2.6.0
is-hotkey: 0.2.0
jotai: 2.8.4(@types/react@18.3.23)(react@18.3.1)
jotai-optics: 0.4.0(jotai@2.8.4(@types/react@18.3.23)(react@18.3.1))(optics-ts@2.4.1)
jotai-optics: 0.4.0(jotai@2.8.4(react@18.3.1))(optics-ts@2.4.1)
jotai-x: 2.3.3(@types/react@18.3.23)(jotai@2.8.4(@types/react@18.3.23)(react@18.3.1))(react@18.3.1)
lodash: 4.17.21
nanoid: 5.1.5
@ -16554,7 +16591,7 @@ snapshots:
slate-react: 0.117.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)
use-deep-compare: 1.3.0(react@18.3.1)
zustand: 5.0.6(@types/react@18.3.23)(immer@10.1.1)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1))
zustand-x: 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(zustand@5.0.6(@types/react@18.3.23)(immer@10.1.1)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)))
zustand-x: 6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(zustand@5.0.6(immer@10.1.1)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)))
transitivePeerDependencies:
- '@types/react'
- immer
@ -16645,7 +16682,7 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@platejs/markdown@49.2.1(platejs@49.1.13(@types/react@18.3.23)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)':
'@platejs/markdown@49.2.1(platejs@49.1.13(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-dom@0.116.0(slate@0.117.0))(slate@0.117.0)(use-sync-external-store@1.5.0(react@18.3.1)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.8.3)':
dependencies:
marked: 15.0.12
mdast-util-math: 3.0.0
@ -19802,6 +19839,8 @@ snapshots:
balanced-match@1.0.2: {}
base64-arraybuffer@1.0.2: {}
base64-js@1.5.1: {}
base64id@2.0.0: {}
@ -20472,6 +20511,10 @@ snapshots:
randombytes: 2.1.0
randomfill: 1.0.4
css-line-break@2.1.0:
dependencies:
utrie: 1.0.2
css-loader@6.11.0(webpack@5.99.9(esbuild@0.25.5)):
dependencies:
icss-utils: 5.1.0(postcss@8.5.6)
@ -22103,6 +22146,11 @@ snapshots:
optionalDependencies:
webpack: 5.99.9(esbuild@0.25.5)
html2canvas-pro@1.5.11:
dependencies:
css-line-break: 2.1.0
text-segmentation: 1.0.3
htmlparser2@10.0.0:
dependencies:
domelementtype: 2.3.0
@ -22527,7 +22575,7 @@ snapshots:
jose@5.10.0: {}
jotai-optics@0.4.0(jotai@2.8.4(@types/react@18.3.23)(react@18.3.1))(optics-ts@2.4.1):
jotai-optics@0.4.0(jotai@2.8.4(react@18.3.1))(optics-ts@2.4.1):
dependencies:
jotai: 2.8.4(@types/react@18.3.23)(react@18.3.1)
optics-ts: 2.4.1
@ -24154,6 +24202,13 @@ snapshots:
sha.js: 2.4.12
to-buffer: 1.2.1
pdf-lib@1.17.1:
dependencies:
'@pdf-lib/standard-fonts': 1.0.0
'@pdf-lib/upng': 1.0.1
pako: 1.0.11
tslib: 1.14.1
performance-now@2.1.0: {}
pg-cloudflare@1.2.7:
@ -25827,6 +25882,10 @@ snapshots:
text-hex@1.0.0: {}
text-segmentation@1.0.3:
dependencies:
utrie: 1.0.2
text-table@0.2.0: {}
thenify-all@1.6.0:
@ -26338,6 +26397,10 @@ snapshots:
utils-merge@1.0.1: {}
utrie@1.0.2:
dependencies:
base64-arraybuffer: 1.0.2
uuid@11.1.0: {}
uuid@8.3.2: {}
@ -26943,7 +27006,7 @@ snapshots:
zod@3.25.1: {}
zustand-x@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(zustand@5.0.6(@types/react@18.3.23)(immer@10.1.1)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1))):
zustand-x@6.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(zustand@5.0.6(immer@10.1.1)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1))):
dependencies:
immer: 10.1.1
lodash.mapvalues: 4.6.0