diff --git a/apps/web/package.json b/apps/web/package.json index 8400fb6dc..509ce86b2 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -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", diff --git a/apps/web/src/components/ui/report/Editor.tsx b/apps/web/src/components/ui/report/Editor.tsx new file mode 100644 index 000000000..39f8cc895 --- /dev/null +++ b/apps/web/src/components/ui/report/Editor.tsx @@ -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; + +export const Editor = React.forwardRef( + ({ className, disabled, focused, variant, ...props }, ref) => { + return ( + + ); + } +); + +Editor.displayName = 'Editor'; + +export function EditorView({ + className, + variant, + ...props +}: PlateViewProps & VariantProps) { + return ; +} + +EditorView.displayName = 'EditorView'; diff --git a/apps/web/src/components/ui/report/EditorContainer.tsx b/apps/web/src/components/ui/report/EditorContainer.tsx index 515747f50..138dff2b9 100644 --- a/apps/web/src/components/ui/report/EditorContainer.tsx +++ b/apps/web/src/components/ui/report/EditorContainer.tsx @@ -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 ( ); diff --git a/apps/web/src/components/ui/report/EditorContent.tsx b/apps/web/src/components/ui/report/EditorContent.tsx deleted file mode 100644 index 719b76fe3..000000000 --- a/apps/web/src/components/ui/report/EditorContent.tsx +++ /dev/null @@ -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 & { variant?: 'default' | 'comment' }) { - return ( - - ); -} diff --git a/apps/web/src/components/ui/report/ReportEditor.tsx b/apps/web/src/components/ui/report/ReportEditor.tsx index d4454cc86..cad687426 100644 --- a/apps/web/src/components/ui/report/ReportEditor.tsx +++ b/apps/web/src/components/ui/report/ReportEditor.tsx @@ -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 ( - {/* - Toolbar is commented out for now. Uncomment and implement as needed. - - - B - - - I - - - U - - - */} - + ); diff --git a/apps/web/src/components/ui/report/all-editor-plugins.ts b/apps/web/src/components/ui/report/all-editor-plugins.ts index 1ba097c52..2e666ee4c 100644 --- a/apps/web/src/components/ui/report/all-editor-plugins.ts +++ b/apps/web/src/components/ui/report/all-editor-plugins.ts @@ -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 ]; diff --git a/apps/web/src/components/ui/report/editor-base-kit.tsx b/apps/web/src/components/ui/report/editor-base-kit.tsx index e7e0c4fc8..b5cf1441d 100644 --- a/apps/web/src/components/ui/report/editor-base-kit.tsx +++ b/apps/web/src/components/ui/report/editor-base-kit.tsx @@ -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'; diff --git a/apps/web/src/components/ui/report/editor-kit.tsx b/apps/web/src/components/ui/report/editor-kit.tsx new file mode 100644 index 000000000..d8950a412 --- /dev/null +++ b/apps/web/src/components/ui/report/editor-kit.tsx @@ -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; + +export const useEditor = () => useEditorRef(); diff --git a/apps/web/src/components/ui/report/elements/Comment.tsx b/apps/web/src/components/ui/report/elements/Comment.tsx index 5ae412d9f..970621400 100644 --- a/apps/web/src/components/ui/report/elements/Comment.tsx +++ b/apps/web/src/components/ui/report/elements/Comment.tsx @@ -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; diff --git a/apps/web/src/components/ui/report/elements/EditorStatic.tsx b/apps/web/src/components/ui/report/elements/EditorStatic.tsx new file mode 100644 index 000000000..8fc85a6bf --- /dev/null +++ b/apps/web/src/components/ui/report/elements/EditorStatic.tsx @@ -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) { + return ; +} diff --git a/apps/web/src/components/ui/report/elements/ExportToolbarButton.tsx b/apps/web/src/components/ui/report/elements/ExportToolbarButton.tsx new file mode 100644 index 000000000..f2a6ea066 --- /dev/null +++ b/apps/web/src/components/ui/report/elements/ExportToolbarButton.tsx @@ -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 = ``; + const katexCss = ``; + + const html = ` + + + + + + + + + ${tailwindCss} + ${katexCss} + + + + ${editorHtml} + + `; + + 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 ( + + + +
+ +
+
+
+ + + + Export as HTML + Export as PDF + Export as Image + Export as Markdown + + +
+ ); +} diff --git a/apps/web/src/components/ui/report/FixedToolbar.tsx b/apps/web/src/components/ui/report/elements/FixedToolbar.tsx similarity index 92% rename from apps/web/src/components/ui/report/FixedToolbar.tsx rename to apps/web/src/components/ui/report/elements/FixedToolbar.tsx index 5537fb05e..73043e7c7 100644 --- a/apps/web/src/components/ui/report/FixedToolbar.tsx +++ b/apps/web/src/components/ui/report/elements/FixedToolbar.tsx @@ -2,7 +2,7 @@ import { cn } from '@/lib/utils'; -import { Toolbar } from './Toolbar'; +import { Toolbar } from '../Toolbar'; export function FixedToolbar({ className, ...props }: React.ComponentProps) { return ( diff --git a/apps/web/src/components/ui/report/elements/FixedToolbarButtons.tsx b/apps/web/src/components/ui/report/elements/FixedToolbarButtons.tsx new file mode 100644 index 000000000..5417100c0 --- /dev/null +++ b/apps/web/src/components/ui/report/elements/FixedToolbarButtons.tsx @@ -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 ( +
+ {!readOnly && ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )} + +
+ + + + + + + + + + + +
+ ); +} diff --git a/apps/web/src/components/ui/report/elements/MentionNode.tsx b/apps/web/src/components/ui/report/elements/MentionNode.tsx new file mode 100644 index 000000000..da64f7e3b --- /dev/null +++ b/apps/web/src/components/ui/report/elements/MentionNode.tsx @@ -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 & { + prefix?: string; + } +) { + const element = props.element; + + const selected = useSelected(); + const focused = useFocused(); + const mounted = useMounted(); + const readOnly = useReadOnly(); + + return ( + + {mounted && IS_APPLE ? ( + // Mac OS IME https://github.com/ianstormtaylor/slate/issues/3490 + + {props.children} + {props.prefix} + {element.value} + + ) : ( + // Others like Android https://github.com/ianstormtaylor/slate/pull/5360 + + {props.prefix} + {element.value} + {props.children} + + )} + + ); +} + +const onSelectItem = getMentionOnSelectItem(); + +export function MentionInputElement(props: PlateElementProps) { + const { editor, element } = props; + const [search, setSearch] = React.useState(''); + + return ( + + + + + + + + No results + + + {MENTIONABLES.map((item) => ( + onSelectItem(editor, item, search)}> + {item.text} + + ))} + + + + + {props.children} + + ); +} + +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' } +]; diff --git a/apps/web/src/components/ui/report/elements/UndoToolbarButton.tsx b/apps/web/src/components/ui/report/elements/UndoToolbarButton.tsx new file mode 100644 index 000000000..bd6206dd6 --- /dev/null +++ b/apps/web/src/components/ui/report/elements/UndoToolbarButton.tsx @@ -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) { + const editor = useEditorRef(); + const disabled = useEditorSelector((editor) => editor.history.redos.length === 0, []); + + return ( + editor.redo()} + onMouseDown={(e) => e.preventDefault()} + tooltip="Redo"> + + + ); +} + +export function UndoToolbarButton(props: React.ComponentProps) { + const editor = useEditorRef(); + const disabled = useEditorSelector((editor) => editor.history.undos.length === 0, []); + + return ( + editor.undo()} + onMouseDown={(e) => e.preventDefault()} + tooltip="Undo"> + + + ); +} diff --git a/apps/web/src/components/ui/report/plugins/basic-markd-kit.tsx b/apps/web/src/components/ui/report/plugins/basic-marks-kit.tsx similarity index 100% rename from apps/web/src/components/ui/report/plugins/basic-markd-kit.tsx rename to apps/web/src/components/ui/report/plugins/basic-marks-kit.tsx diff --git a/apps/web/src/components/ui/report/plugins/dnd-kit-new.tsx b/apps/web/src/components/ui/report/plugins/dnd-kit.tsx similarity index 100% rename from apps/web/src/components/ui/report/plugins/dnd-kit-new.tsx rename to apps/web/src/components/ui/report/plugins/dnd-kit.tsx diff --git a/apps/web/src/components/ui/report/plugins/docx-kit.tsx b/apps/web/src/components/ui/report/plugins/docx-kit.tsx new file mode 100644 index 000000000..f251b90b5 --- /dev/null +++ b/apps/web/src/components/ui/report/plugins/docx-kit.tsx @@ -0,0 +1,6 @@ +'use client'; + +import { DocxPlugin } from '@platejs/docx'; +import { JuicePlugin } from '@platejs/juice'; + +export const DocxKit = [DocxPlugin, JuicePlugin]; diff --git a/apps/web/src/components/ui/report/plugins/fixed-toolbar-kit.tsx b/apps/web/src/components/ui/report/plugins/fixed-toolbar-kit.tsx new file mode 100644 index 000000000..654c49cec --- /dev/null +++ b/apps/web/src/components/ui/report/plugins/fixed-toolbar-kit.tsx @@ -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: () => ( + + + + ) + } + }) +]; diff --git a/apps/web/src/components/ui/report/plugins/markdown-kit.tsx b/apps/web/src/components/ui/report/plugins/markdown-kit.tsx new file mode 100644 index 000000000..c4cb0a07a --- /dev/null +++ b/apps/web/src/components/ui/report/plugins/markdown-kit.tsx @@ -0,0 +1,3 @@ +import { MarkdownPlugin } from '@platejs/markdown'; + +export const MarkdownKit = [MarkdownPlugin]; diff --git a/apps/web/src/components/ui/report/plugins/markdown-kit/index.ts b/apps/web/src/components/ui/report/plugins/markdown-kit/index.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/web/src/components/ui/report/plugins/mention-kit.tsx b/apps/web/src/components/ui/report/plugins/mention-kit.tsx new file mode 100644 index 000000000..7ae8c1ca5 --- /dev/null +++ b/apps/web/src/components/ui/report/plugins/mention-kit.tsx @@ -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) +]; diff --git a/apps/web/src/hooks/useMount.ts b/apps/web/src/hooks/useMount.ts index 6a0acf4b5..0594005b4 100644 --- a/apps/web/src/hooks/useMount.ts +++ b/apps/web/src/hooks/useMount.ts @@ -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; +}; diff --git a/packages/server-shared/src/lib/report/markdown-to-platejs.ts b/packages/server-shared/src/lib/report/markdown-to-platejs.ts index f95a1bd88..1a0998e47 100644 --- a/packages/server-shared/src/lib/report/markdown-to-platejs.ts +++ b/packages/server-shared/src/lib/report/markdown-to-platejs.ts @@ -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 = [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 840312f0f..04993324c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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