@wair/editor 0.0.1-beta.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Heading1","Heading2","Heading3","Heading4","Heading5","Heading6","undo: IActionItem","Undo2","redo: IActionItem","Redo2","paragraph: IActionItem","Type","blockquote: IActionItem","Quote","codeBlock: IActionItem","CodeXml","orderedList: IActionItem","ListOrdered","bulletList: IActionItem","List","todoList: IActionItem","ListTodo","leftAlign: IActionItem","AlignLeft","rightAlign: IActionItem","AlignRight","centerAlign: IActionItem","AlignCenter","justifyAlign: IActionItem","AlignJustify","textAlignActions: IActionItem[]","bold: IActionItem","BoldIcon","italic: IActionItem","ItalicIcon","underline: IActionItem","UnderlineIcon","strike: IActionItem","StrikethroughIcon","inlineCode: IActionItem","Code","textFormatActions: IActionItem[]","nodeFormatActions: IActionItem[]","Toggle: React.ForwardRefExoticComponent<\n ToggleProps & React.RefAttributes<React.ElementRef<typeof TogglePrimitive.Root>>\n>","React","TogglePrimitive","TooltipPrimitive","React","Slot","React","PopoverPrimitive","editor","nodeFormatActions","ChevronDown","Check","textFormatActions","LabelPrimitive","React","LinkIcon","TEXT_COLORS: string[]","editor","Popover","Baseline","ChevronDown","HIGHLIGHT_COLORS: string[]","editor","Popover","Highlighter","ChevronDown","ChevronDown","Check","ImagePlus","React","Table","SeparatorPrimitive","DEFAULT_CONFIG: ToolbarItemKey[]","NodeSelector","TextSelector","LinkSelector","ColorSelector","BgSelector","AlignSelector","InsetImageSelector","TableActionSelector","React","DropdownMenuPrimitive","Check","Copy","ChevronDown","ChevronRight","NodeViewContent","NodeViewWrapper","DialogPrimitive","XIcon","Maximize","Square","AspectRatio","FlipVertical","FlipHorizontal","RotateCcwSquare","RotateCwSquare","RefreshCw","X","Check","Cropper","React","React","ImageActions: React.FC<any>","AlignLeft","AlignCenter","AlignRight","Maximize","Trash2","React","LoaderCircle","React","directionStyles","validFiles: T[]","errors: FileError[]","ImageViewBlock: React.FC<NodeViewProps>","React","NodeViewWrapper","LoaderCircle","ControlledZoom","Trash2","TiptapImage","TiptapLink","Plugin","TextSelection","defaultUiState: UiState","Extension","node","TableMap","CellSelection","Node","Plugin","DecorationSet","decorations: Decoration[]","Decoration","TiptapTableHeader","Plugin","DecorationSet","decorations: Decoration[]","Decoration","TiptapTableRow","TipTable","lowlight","common","html","css","js","ts","TextStyleKit","StarterKit","Placeholder","Focus","TextAlign","Color","BackgroundColor","TaskList","TaskItem","Table","Gapcursor","Image","props","Dropcursor","FileHandler","Selection","Link","CodeBlockLowlight","Mathematics","BubbleMenu","NodeSelection","TextSelector","LinkSelector","ColorSelector","BgSelector","BubbleMenuBlock: React.FC<any>","props","DragHandle","GripVertical","Menus","Copy","Trash2","editor","BubbleMenu","ArrowLeftToLine","ArrowRightToLine","Trash2","ArrowUpToLine","ArrowDownToLine","TableCellsMerge","TableCellsSplit","editor","BubbleMenuText","BubbleMenuTable","BubbleMenuBlock","EditorContent"],"sources":["../src/context/index.tsx","../src/toolbarAction.tsx","../src/lib/utils.ts","../src/components/ui/toggle.tsx","../src/components/ui/tooltip.tsx","../src/components/ui/button.tsx","../src/toolbar/components/action-button/index.tsx","../src/components/ui/popover.tsx","../src/hooks/useEditorDerivedState.ts","../src/toolbar/components/selectors/node-selector.tsx","../src/toolbar/components/selectors/text-selector.tsx","../src/components/ui/label.tsx","../src/components/ui/input.tsx","../src/extensions/extension-link/widget/edit-panel.tsx","../src/toolbar/components/selectors/link-selector.tsx","../src/toolbar/components/selectors/color-selector.tsx","../src/toolbar/components/selectors/bg-selector.tsx","../src/toolbar/components/selectors/align-selector.tsx","../src/toolbar/components/selectors/inset-image-selector.tsx","../src/utils/constant.ts","../src/toolbar/components/selectors/table-action-selector.tsx","../src/components/ui/separator.tsx","../src/toolbar/index.tsx","../src/components/ui/dropdown-menu.tsx","../src/extensions/extension-code-block/code-block.tsx","../src/extensions/extension-image/hooks/use-drag-resize.ts","../src/components/ui/dialog.tsx","../src/extensions/extension-image/widget/image-cropper.tsx","../src/extensions/extension-image/widget/image-actions.tsx","../src/extensions/extension-image/widget/image-overlay.tsx","../src/extensions/extension-image/widget/resize-handle.tsx","../src/extensions/extension-image/utils.ts","../src/extensions/extension-image/widget/image-view-block.tsx","../src/extensions/extension-image/index.ts","../src/extensions/extension-link/index.ts","../src/extensions/ui-state-extension.ts","../src/extensions/extension-table/utils.tsx","../src/extensions/extension-table/cell.tsx","../src/extensions/extension-table/header.tsx","../src/extensions/extension-table/row.tsx","../src/extensions/extension-table/index.tsx","../src/extensions/index.ts","../src/bubble-menu/bubble-menu-text/index.tsx","../src/bubble-menu/bubble-menu-block/menus.tsx","../src/bubble-menu/bubble-menu-block/index.tsx","../src/bubble-menu/bubble-menu-table/index.tsx","../src/editor/index.tsx"],"sourcesContent":["\"use client\";\nimport React, { createContext, useContext } from \"react\";\nimport { Editor } from \"@tiptap/react\";\n\nconst EditorRoot = createContext({} as { editor: Editor });\n\nexport const useCurrentEditor = () => {\n const context = useContext(EditorRoot);\n if (!context) {\n throw new Error(\"useCurrentEditor 必须在 EditorProvider 内部使用\");\n }\n if (!context.editor) {\n throw new Error(\"EditorProvider 未提供 editor\");\n }\n\n return context;\n};\n\nexport const EditorProvider = ({\n editor,\n children,\n}: React.PropsWithChildren<{ editor: Editor }>) => {\n if (!editor) return null;\n return <EditorRoot.Provider value={{ editor }}>{children}</EditorRoot.Provider>;\n};\n","import {\n CodeXml,\n Heading1,\n Heading2,\n Heading3,\n Heading4,\n Heading5,\n Heading6,\n List,\n ListOrdered,\n Quote,\n Undo2,\n Redo2,\n ListTodo,\n Type,\n type LucideIcon,\n AlignLeft,\n AlignRight,\n AlignCenter,\n AlignJustify,\n UnderlineIcon,\n BoldIcon,\n ItalicIcon,\n StrikethroughIcon,\n Code,\n} from \"lucide-react\";\nimport { type Editor } from \"@tiptap/react\";\nimport { Level } from \"@tiptap/extension-heading\";\nimport { type IEditorDerivedState } from \"./hooks/useEditorDerivedState\";\n\nexport type IActionItem = {\n label: string;\n icon: LucideIcon;\n id: string;\n onClick: (editor: Editor) => void;\n isActive?: (state: IEditorDerivedState) => boolean;\n disabled?: (state: IEditorDerivedState) => boolean;\n shortcutKeys?: string[];\n};\n\nconst HeadingIcon = {\n 1: Heading1,\n 2: Heading2,\n 3: Heading3,\n 4: Heading4,\n 5: Heading5,\n 6: Heading6,\n};\n\nexport const getHeadingByLevel = (level: Level): IActionItem => ({\n label: `标题${level}`,\n icon: HeadingIcon[level],\n id: `heading${level}`,\n onClick: (editor) => editor.chain().focus().clearNodes().setHeading({ level }).run(),\n isActive: (state) => state[`isHeading${level}`],\n // shortcutKeys: [\"alt\", \"mod\", `${level}`],\n});\n\nexport const undo: IActionItem = {\n label: \"撤销\",\n icon: Undo2,\n id: \"undo\",\n onClick: (editor) => editor.chain().focus().undo().run(),\n disabled: (state) => {\n return !state.canUndo;\n },\n shortcutKeys: [\"mod\", \"Z\"],\n};\n\nexport const redo: IActionItem = {\n label: \"重做\",\n icon: Redo2,\n id: \"redo\",\n onClick: (editor) => editor.chain().focus().redo().run(),\n disabled: (state) => {\n return !state.canRedo;\n },\n shortcutKeys: [\"mod\", \"shift\", \"Z\"],\n};\n\nexport const paragraph: IActionItem = {\n label: \"正文\",\n icon: Type,\n id: \"paragraph\",\n onClick: (editor) => editor.chain().focus().clearNodes().setParagraph().run(),\n isActive: (state) => state.isParagraph,\n};\n\nexport const blockquote: IActionItem = {\n label: \"引用\",\n icon: Quote,\n id: \"blockquote\",\n onClick: (editor) => editor.chain().focus().clearNodes().setBlockquote().run(),\n isActive: (state) => state.isBlockquote,\n // shortcutKeys: [\"shift\", \"mod\", \"B\"],\n};\n\nexport const codeBlock: IActionItem = {\n label: \"代码块\",\n icon: CodeXml,\n id: \"codeBlock\",\n onClick: (editor) => editor.chain().focus().clearNodes().setCodeBlock().run(),\n isActive: (state) => state.isCodeBlock,\n};\n\nexport const orderedList: IActionItem = {\n label: \"有序列表\",\n icon: ListOrdered,\n id: \"orderedList\",\n onClick: (editor) => editor.chain().focus().clearNodes().toggleOrderedList().run(),\n isActive: (state) => state.isOrderedList,\n};\n\nexport const bulletList: IActionItem = {\n label: \"无序列表\",\n icon: List,\n id: \"bulletList\",\n onClick: (editor) => editor.chain().focus().clearNodes().toggleBulletList().run(),\n isActive: (state) => state.isBulletList,\n};\n\nexport const todoList: IActionItem = {\n label: \"待办任务\",\n icon: ListTodo,\n id: \"todoList\",\n onClick: (editor) => editor.chain().focus().clearNodes().toggleTaskList().run(),\n isActive: (editor) => editor.isTaskList,\n // shortcutKeys: [\"shift\", \"mod\", \"9\"],\n};\n\nexport const toolbarNodeConfig = {\n select: [\n getHeadingByLevel(1),\n getHeadingByLevel(2),\n getHeadingByLevel(3),\n getHeadingByLevel(4),\n getHeadingByLevel(5),\n paragraph,\n blockquote,\n codeBlock,\n ],\n out: [bulletList, orderedList, todoList],\n};\n\nexport const leftAlign: IActionItem = {\n label: \"左对齐\",\n icon: AlignLeft,\n id: \"leftAlign\",\n onClick: (editor) => editor.chain().setTextAlign(\"left\").run(),\n isActive: (state) => state.isAlignLeft,\n};\nexport const rightAlign: IActionItem = {\n label: \"右对齐\",\n icon: AlignRight,\n id: \"rightAlign\",\n onClick: (editor) => editor.chain().setTextAlign(\"right\").run(),\n isActive: (state) => state.isAlignRight,\n};\nexport const centerAlign: IActionItem = {\n label: \"居中对齐\",\n icon: AlignCenter,\n id: \"centerAlign\",\n onClick: (editor) => editor.chain().setTextAlign(\"center\").run(),\n isActive: (state) => state.isAlignCenter,\n};\nexport const justifyAlign: IActionItem = {\n label: \"两端对齐\",\n icon: AlignJustify,\n id: \"justifyAlign\",\n onClick: (editor) => editor.chain().setTextAlign(\"justify\").run(),\n isActive: (state) => state.isAlignJustify,\n};\n\nexport const textAlignActions: IActionItem[] = [leftAlign, rightAlign, centerAlign, justifyAlign];\n\nexport const bold: IActionItem = {\n label: \"加粗\",\n icon: BoldIcon,\n id: \"bold\",\n onClick: (editor) => editor.chain().focus().toggleBold().run(),\n isActive: (editor) => editor.isBold,\n disabled: (state) => !state.canBold,\n shortcutKeys: [\"mod\", \"B\"],\n};\n\nexport const italic: IActionItem = {\n label: \"斜体\",\n icon: ItalicIcon,\n id: \"italic\",\n onClick: (editor) => editor.chain().focus().toggleItalic().run(),\n isActive: (editor) => editor.isItalic,\n disabled: (state) => !state.canItalic,\n shortcutKeys: [\"mod\", \"I\"],\n};\n\nexport const underline: IActionItem = {\n label: \"下划线\",\n icon: UnderlineIcon,\n id: \"underline\",\n onClick: (editor) => editor.chain().focus().toggleUnderline().run(),\n isActive: (editor) => editor.isUnderline,\n disabled: (state) => !state.canUnderline,\n shortcutKeys: [\"mod\", \"U\"],\n};\n\nexport const strike: IActionItem = {\n label: \"删除线\",\n icon: StrikethroughIcon,\n id: \"strike\",\n onClick: (editor) => editor.chain().focus().toggleStrike().run(),\n isActive: (editor) => editor.isStrike,\n disabled: (state) => !state.canStrike,\n shortcutKeys: [\"mod\", \"shift\", \"S\"],\n};\n\nexport const inlineCode: IActionItem = {\n label: \"行内代码\",\n icon: Code,\n id: \"inlineCode\",\n onClick: (editor) => editor.chain().focus().toggleCode().run(),\n isActive: (editor) => editor.isCode,\n disabled: (state) => !state.canCode,\n shortcutKeys: [\"mod\", \"E\"],\n};\n\nexport const textFormatActions: IActionItem[] = [bold, italic, underline, strike, inlineCode];\n\nexport const nodeFormatActions: IActionItem[] = [\n getHeadingByLevel(1),\n getHeadingByLevel(2),\n getHeadingByLevel(3),\n getHeadingByLevel(4),\n getHeadingByLevel(5),\n paragraph,\n blockquote,\n codeBlock,\n bulletList,\n orderedList,\n todoList,\n];\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst toggleVariants = cva(\n \"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap\",\n {\n variants: {\n variant: {\n default: \"bg-transparent\",\n outline:\n \"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground\",\n },\n size: {\n default: \"h-9 px-2 min-w-9\",\n sm: \"h-8 px-1.5 min-w-8\",\n lg: \"h-10 px-2.5 min-w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\ntype ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\n VariantProps<typeof toggleVariants>\n\nconst Toggle: React.ForwardRefExoticComponent<\n ToggleProps & React.RefAttributes<React.ElementRef<typeof TogglePrimitive.Root>>\n> = React.forwardRef<\n React.ElementRef<typeof TogglePrimitive.Root>,\n ToggleProps\n>(({ className, variant, size, ...props }, ref) => (\n <TogglePrimitive.Root\n ref={ref}\n data-slot=\"toggle\"\n className={cn(toggleVariants({ variant, size, className }))}\n {...props}\n />\n))\n\nToggle.displayName = TogglePrimitive.Root.displayName\n\nexport { Toggle, toggleVariants };\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction TooltipProvider({\n delayDuration = 0,\n ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n return (\n <TooltipPrimitive.Provider\n data-slot=\"tooltip-provider\"\n delayDuration={delayDuration}\n {...props}\n />\n );\n}\n\nfunction Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n return (\n <TooltipProvider>\n <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\n </TooltipProvider>\n );\n}\n\nfunction TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\n return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />;\n}\n\nfunction TooltipContent({\n className,\n sideOffset = 0,\n children,\n ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\n return (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n data-slot=\"tooltip-content\"\n sideOffset={sideOffset}\n className={cn(\n \"origin-(--radix-tooltip-content-transform-origin) z-50 w-fit text-balance rounded-md bg-foreground px-3 py-1.5 text-xs text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n >\n {children}\n <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground\" />\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n );\n}\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n","import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nconst Button = React.forwardRef<\n HTMLButtonElement, // 声明 ref 指向的类型\n React.ComponentProps<\"button\"> & VariantProps<typeof buttonVariants> & { asChild?: boolean }\n>(({ className, variant = \"default\", size = \"default\", asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref} // 关键:将 ref 转发给真实的 DOM 元素或 Slot\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n});\n\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","import * as React from \"react\";\nimport type { TooltipContentProps } from \"@radix-ui/react-tooltip\";\nimport { Toggle } from \"../../../components/ui/toggle\";\nimport { cn } from \"../../../lib/utils\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"../../../components/ui/tooltip\";\nimport { Button } from \"../../../components/ui/button\";\n\ninterface ToolbarButtonProps extends React.ComponentPropsWithoutRef<typeof Toggle> {\n isActive?: boolean;\n tooltip?: string;\n tooltipOptions?: TooltipContentProps;\n className?: string;\n shortcutKeys?: string[];\n}\n\nexport const ToolbarButton = React.forwardRef<\n HTMLButtonElement,\n ToolbarButtonProps & React.PropsWithChildren\n>(({ isActive, children, tooltip, className, tooltipOptions, shortcutKeys, ...props }, ref) => {\n const toggleButton = (\n <Toggle\n size=\"sm\"\n ref={ref}\n className={cn(\n \"text-content-first size-7 gap-1 rounded border-none p-0\",\n { \"bg-primary/5\": isActive },\n className\n )}\n {...props}\n >\n {children}\n </Toggle>\n );\n\n if (!tooltip) {\n return toggleButton;\n }\n\n return (\n <TooltipProvider delayDuration={300}>\n <Tooltip>\n <TooltipTrigger asChild>{toggleButton}</TooltipTrigger>\n <TooltipContent {...tooltipOptions}>\n <div className=\"flex flex-col items-center text-center\">{tooltip}</div>\n </TooltipContent>\n </Tooltip>\n </TooltipProvider>\n );\n});\n\nToolbarButton.displayName = \"ToolbarButton\";\n\ninterface ActionButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n children: React.ReactNode;\n tooltip: string;\n tooltipOptions?: TooltipContentProps;\n}\n\nexport const ActionButton = React.memo(\n React.forwardRef<HTMLButtonElement, ActionButtonProps>(\n ({ children, tooltip, className, ...props }, ref) => (\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger>\n <Button\n ref={ref}\n variant=\"ghost\"\n className={cn(\n \"text-content-first relative flex size-7 flex-row rounded p-0\",\n \"bg-transparent hover:bg-transparent\",\n className\n )}\n {...props}\n >\n {children}\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">{tooltip}</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n )\n )\n);\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />;\n}\n\nfunction PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\n}\n\nfunction PopoverContent({\n className,\n align = \"center\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"origin-(--radix-popover-content-transform-origin) outline-hidden z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n );\n}\n\nfunction PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />;\n}\n\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };\n","import { useEditorState } from \"@tiptap/react\";\nimport { useCurrentEditor } from \"../context\";\n\nexport interface IEditorDerivedState {\n // ---- text format ----\n isBold: boolean;\n canBold: boolean;\n isItalic: boolean;\n canItalic: boolean;\n isUnderline: boolean;\n canUnderline: boolean;\n isStrike: boolean;\n canStrike: boolean;\n isCode: boolean;\n canCode: boolean;\n isHeading1: boolean;\n isHeading2: boolean;\n isHeading3: boolean;\n isHeading4: boolean;\n isHeading5: boolean;\n isHeading6: boolean;\n // ---- node format ----\n isParagraph: boolean;\n isBulletList: boolean;\n isOrderedList: boolean;\n isTaskList: boolean;\n isBlockquote: boolean;\n isCodeBlock: boolean;\n // ---- history ----\n canUndo: boolean;\n canRedo: boolean;\n\n // ---- align ----\n isAlignLeft: boolean;\n isAlignRight: boolean;\n isAlignCenter: boolean;\n isAlignJustify: boolean;\n // ---- table ----\n isTable: boolean;\n canTable: boolean;\n}\n\nexport const useEditorDerivedState = () => {\n const { editor } = useCurrentEditor();\n\n return useEditorState<IEditorDerivedState>({\n editor,\n selector: (ctx) => {\n const { editor } = ctx;\n const is = (name: unknown, attrs = {}) =>\n editor.isActive(name as unknown as string, attrs) ?? false;\n const can = (name: string, attrs = {}) =>\n (editor.can().chain() as unknown as any)[name]?.(attrs)?.run();\n\n return {\n // ---- text format ----\n isBold: is(\"bold\"),\n canBold: can(\"toggleBold\"),\n isItalic: is(\"italic\"),\n canItalic: can(\"toggleItalic\"),\n isUnderline: is(\"underline\"),\n canUnderline: can(\"toggleUnderline\"),\n isStrike: is(\"strike\"),\n canStrike: can(\"toggleStrike\"),\n isCode: is(\"code\"),\n canCode: can(\"toggleCode\"),\n\n // ---- node format ----\n isParagraph: is(\"paragraph\"),\n isHeading1: is(\"heading\", { level: 1 }),\n isHeading2: is(\"heading\", { level: 2 }),\n isHeading3: is(\"heading\", { level: 3 }),\n isHeading4: is(\"heading\", { level: 4 }),\n isHeading5: is(\"heading\", { level: 5 }),\n isHeading6: is(\"heading\", { level: 6 }),\n isBulletList: is(\"bulletList\"),\n isOrderedList: is(\"orderedList\"),\n isTaskList: is(\"taskList\"),\n isBlockquote: is(\"blockquote\"),\n isCodeBlock: is(\"codeBlock\"),\n\n // ---- history ----\n canUndo: editor.can().undo(),\n canRedo: editor.can().redo(),\n\n // ---- align ----\n isAlignLeft: is({ textAlign: \"left\" }),\n isAlignRight: is({ textAlign: \"right\" }),\n isAlignCenter: is({ textAlign: \"center\" }),\n isAlignJustify: is({ textAlign: \"justify\" }),\n\n // ---- table ----\n isTable: is(\"table\"),\n canTable: can(\"insertTable\"),\n } as IEditorDerivedState;\n },\n });\n};\n","import { memo } from \"react\";\nimport { useEditorState } from \"@tiptap/react\";\n\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../../components/ui/popover\";\nimport { Button } from \"../../../components/ui/button\";\nimport { useCurrentEditor } from \"../../../context\";\nimport { cn } from \"../../../lib/utils\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport { IActionItem, paragraph } from \"../../../toolbarAction\";\nimport { useEditorDerivedState } from \"../../../hooks/useEditorDerivedState\";\n\ninterface IProps {\n nodeFormatActions: IActionItem[];\n}\n\nconst NodeSelector = ({ nodeFormatActions }: IProps) => {\n const { editor } = useCurrentEditor();\n const state = useEditorDerivedState();\n const activeItem =\n nodeFormatActions.find((item) => {\n return item.isActive?.(state);\n }) ?? paragraph;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"ghost\" className=\"h-7 gap-1 rounded px-1\">\n <activeItem.icon size={20} />\n <ChevronDown size={12} color=\"#9fa2ab\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent\n className=\"border-line z-50 w-40 rounded-xl border bg-white px-2 py-1 shadow-xl\"\n align=\"start\"\n sideOffset={18}\n >\n {nodeFormatActions.map((item) => {\n return (\n <div\n key={item.id}\n onClick={() => {\n item.onClick(editor);\n }}\n className={cn(\n \"flex cursor-pointer items-center rounded-sm p-2 text-sm hover:bg-accent\",\n activeItem.label === item.label ? \"text-primary\" : \"\"\n )}\n >\n <item.icon size=\"20\" className=\"mr-3\" />\n <span>{item.label}</span>\n {activeItem.label === item.label && <Check size=\"16\" className=\"ml-auto items-end\" />}\n </div>\n );\n })}\n </PopoverContent>\n </Popover>\n );\n};\n\nexport default memo(NodeSelector);\n","import { memo } from \"react\";\nimport { useCurrentEditor } from \"../../../context\";\nimport { IActionItem } from \"../../../toolbarAction\";\nimport { ToolbarButton } from \"../action-button\";\nimport type { TooltipContentProps } from \"@radix-ui/react-tooltip\";\nimport { useEditorDerivedState } from \"../../../hooks/useEditorDerivedState\";\n\ninterface IProps {\n textFormatActions: IActionItem[];\n tooltipOptions?: TooltipContentProps;\n}\n\nconst TextSelector = ({ textFormatActions, tooltipOptions }: IProps) => {\n const { editor } = useCurrentEditor();\n const state = useEditorDerivedState();\n\n return (\n <>\n {textFormatActions.map((item) => (\n <ToolbarButton\n key={item.id}\n isActive={item.isActive?.(state)}\n onClick={() => item.onClick(editor)}\n disabled={item.disabled?.(state)}\n tooltip={item.label}\n tooltipOptions={tooltipOptions}\n shortcutKeys={item.shortcutKeys}\n >\n <item.icon size={20} />\n </ToolbarButton>\n ))}\n </>\n );\n};\n\nexport default memo(TextSelector);\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\n \"flex select-none items-center gap-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Label };\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n \"shadow-xs h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Input };\n","import * as React from \"react\";\nimport { Button } from \"../../../components/ui/button\";\nimport { Label } from \"../../../components/ui/label\";\nimport { Input } from \"../../../components/ui/input\";\nimport { cn } from \"../../../lib/utils\";\n\nexport interface LinkEditorProps extends React.HTMLAttributes<HTMLDivElement> {\n defaultUrl?: string;\n defaultText?: string;\n onSave: (url: string, text?: string, isNewTab?: boolean) => void;\n}\n\nexport const LinkEditPanel = React.forwardRef<HTMLDivElement, LinkEditorProps>(\n ({ onSave, defaultUrl, defaultText, className }, ref) => {\n const formRef = React.useRef<HTMLDivElement>(null);\n const [url, setUrl] = React.useState(defaultUrl || \"\");\n const [text, setText] = React.useState(defaultText || \"\");\n\n const handleSave = React.useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n if (formRef.current) {\n const isValid = Array.from(formRef.current.querySelectorAll(\"input\")).every((input) =>\n input.checkValidity()\n );\n\n if (isValid) {\n onSave(url, text);\n } else {\n formRef.current.querySelectorAll(\"input\").forEach((input) => {\n if (!input.checkValidity()) {\n input.reportValidity();\n }\n });\n }\n }\n },\n [onSave, url, text]\n );\n\n React.useImperativeHandle(ref, () => formRef.current as HTMLDivElement);\n\n return (\n <div className=\"shadow-box w-full min-w-80 rounded-xl p-4\" ref={formRef}>\n <div className={cn(\"space-y-4\", className)}>\n <div className=\"space-y-1\">\n <Label>链接</Label>\n <Input\n type=\"url\"\n required\n placeholder=\"输入链接\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n handleSave(e); // 按回车键时触发保存\n }\n }}\n />\n </div>\n\n <div className=\"space-y-1\">\n <Label>文本</Label>\n <Input\n type=\"text\"\n placeholder=\"输入文本\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n handleSave(e); // 按回车键时触发保存\n }\n }}\n />\n </div>\n\n <div className=\"flex justify-end space-x-2\">\n <Button type=\"button\" onClick={handleSave}>\n 保存\n </Button>\n </div>\n </div>\n </div>\n );\n }\n);\n\nLinkEditPanel.displayName = \"LinkEditPanel\";\n","\"use client\";\nimport React, { useCallback } from \"react\";\nimport { LinkIcon } from \"lucide-react\";\nimport { ToolbarButton } from \"../action-button\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../../components/ui/popover\";\nimport { LinkEditPanel } from \"../../../extensions/extension-link/widget/edit-panel\";\nimport { cn } from \"../../../lib/utils\";\nimport { useEditorState } from \"@tiptap/react\";\nimport { useCurrentEditor } from \"../../../context\";\nimport { Button } from \"../../../components/ui/button\";\n\nconst LinkSelector = ({ tooltipOptions }: { tooltipOptions?: any }) => {\n const { editor } = useCurrentEditor();\n\n const { href, text, isActive } = useEditorState({\n editor,\n selector: (ctx) => {\n const { from, to } = ctx.editor.state.selection;\n const { href } = ctx.editor.getAttributes(\"link\");\n const text = editor.state.doc.textBetween(from, to, \" \");\n\n return {\n href,\n text,\n isActive: editor.isActive(\"link\"),\n };\n },\n });\n\n const onSetLink = useCallback(\n (href: string, text?: string) => {\n editor\n .chain()\n .extendMarkRange(\"link\")\n .insertContent({\n type: \"text\",\n text,\n marks: [\n {\n type: \"link\",\n attrs: {\n href,\n target: \"_blank\",\n },\n },\n ],\n })\n .setLink({ href, target: \"_blank\" })\n .focus()\n .run();\n },\n [editor]\n );\n\n return (\n <Popover defaultOpen={isActive}>\n <PopoverTrigger asChild>\n <ToolbarButton\n pressed={isActive}\n isActive={isActive}\n tooltip=\"插入链接\"\n tooltipOptions={tooltipOptions}\n >\n <LinkIcon size={19} />\n </ToolbarButton>\n </PopoverTrigger>\n <PopoverContent align=\"center\" className=\"z-50 w-max\" sideOffset={18}>\n <LinkEditPanel defaultUrl={href} defaultText={text} onSave={onSetLink} />\n </PopoverContent>\n </Popover>\n );\n};\n\nexport default LinkSelector;\n","import { Baseline, ChevronDown } from \"lucide-react\";\nimport * as Popover from \"@radix-ui/react-popover\";\nimport { cn } from \"../../../lib/utils\";\nimport { useEditorState } from \"@tiptap/react\";\nimport { useCurrentEditor } from \"../../../context\";\nimport { Button } from \"../../../components/ui/button\";\n\nconst TEXT_COLORS: string[] = [\n \"#111111\",\n \"#414141\",\n \"#707070\",\n \"#B8B8B8\",\n \"#E7E7E7\",\n \"#FFFFFF\",\n \"#4D0000\",\n \"#800000\",\n \"#B30000\",\n \"#FF0000\",\n \"#FF4C4C\",\n \"#FFB2B2\",\n \"#4D2D16\",\n \"#804B25\",\n \"#B36934\",\n \"#FF964A\",\n \"#FFB580\",\n \"#FFDFC9\",\n \"#4D4110\",\n \"#806D1B\",\n \"#B39926\",\n \"#FFDA36\",\n \"#FFE572\",\n \"#FFF4C3\",\n \"#084623\",\n \"#0D7539\",\n \"#12A451\",\n \"#19EA73\",\n \"#5EF09D\",\n \"#BAF9D5\",\n \"#003845\",\n \"#005E72\",\n \"#0083A0\",\n \"#00BBE5\",\n \"#4CCFED\",\n \"#B2EBF7\",\n \"#021C4D\",\n \"#032F80\",\n \"#0542B3\",\n \"#075EFF\",\n \"#518EFF\",\n \"#B5CFFF\",\n \"#36204D\",\n \"#5A3580\",\n \"#7E4AB3\",\n \"#B46AFF\",\n \"#CA97FF\",\n \"#E8D2FF\",\n\n // \"#111111\",\n // \"#707070\",\n // \"#B8B8B8\",\n // \"#B30000\",\n // \"#B36934\",\n // \"#B39926\",\n // \"#12A451\",\n // \"#0083A0\",\n // \"#0542B3\",\n // \"#7E4AB3\",\n];\n\nconst ColorSelector = () => {\n const { editor } = useCurrentEditor();\n\n const { activeColorItem } = useEditorState({\n editor,\n selector: ({ editor }) => {\n return {\n activeColorItem: TEXT_COLORS.find((color) => editor.isActive(\"textStyle\", { color })),\n };\n },\n });\n\n return (\n <Popover.Root>\n <Popover.Trigger asChild>\n <Button variant=\"ghost\" className=\"h-7 gap-1 rounded px-1\">\n <Baseline size={20} color={activeColorItem} />\n <ChevronDown size={12} color=\"#9fa2ab\" />\n </Button>\n </Popover.Trigger>\n\n <Popover.Content\n className=\"border-line shadow-box z-50 w-[242px] rounded-xl border bg-white p-4\"\n align=\"center\"\n sideOffset={18}\n >\n <div>\n <div\n className=\"text-content-first border-line mb-[10px] flex h-8 cursor-pointer items-center justify-center rounded-lg border text-sm\"\n onClick={() => {\n editor.chain().focus().unsetColor().run();\n }}\n >\n 默认颜色\n </div>\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n {TEXT_COLORS.map((color: string) => (\n <span\n key={color}\n className={cn(\n \"hover:border-content-first flex h-7 w-7 cursor-pointer items-center justify-center rounded-sm border border-transparent\",\n {\n \"border-content-first\": activeColorItem === color,\n }\n )}\n onClick={() => {\n editor.chain().focus().unsetColor().run();\n editor.chain().focus().setColor(color).run();\n }}\n >\n <span\n className=\"border-line h-6 w-6 rounded-sm border\"\n style={{\n backgroundColor: color,\n }}\n />\n </span>\n ))}\n </div>\n </div>\n </Popover.Content>\n </Popover.Root>\n );\n};\n\nexport default ColorSelector;\n","import { ChevronDown, Highlighter } from \"lucide-react\";\nimport * as Popover from \"@radix-ui/react-popover\";\n\nimport { useEditorState } from \"@tiptap/react\";\nimport { useCurrentEditor } from \"../../../context\";\nimport { Button } from \"../../../components/ui/button\";\nimport { cn } from \"../../../lib/utils\";\n\nconst HIGHLIGHT_COLORS: string[] = [\n \"#FF0000\",\n \"#FF4C4C\",\n \"#FFB2B2\",\n \"#FF964A\",\n \"#FFB580\",\n \"#FFDFC9\",\n \"#FFDA36\",\n \"#FFE572\",\n \"#FFF4C3\",\n \"#19EA73\",\n \"#5EF09D\",\n \"#BAF9D5\",\n \"#00BBE5\",\n \"#4CCFED\",\n \"#B2EBF7\",\n \"#075EFF\",\n \"#518EFF\",\n \"#B5CFFF\",\n \"#B46AFF\",\n \"#CA97FF\",\n \"#E8D2FF\",\n \"#707070\",\n \"#B8B8B8\",\n \"#E7E7E7\",\n // ...docxHighlight\n];\n\nconst BgSelector = () => {\n const { editor } = useCurrentEditor();\n\n const { activeHighlightItem } = useEditorState({\n editor,\n selector: ({ editor }) => {\n return {\n activeHighlightItem: HIGHLIGHT_COLORS.find((color) =>\n editor.isActive(\"highlight\", { color })\n ),\n };\n },\n });\n\n return (\n <Popover.Root>\n <Popover.Trigger asChild>\n <Button variant=\"ghost\" className=\"h-7 gap-1 rounded px-1\">\n <Highlighter size={20} color={activeHighlightItem} />\n <ChevronDown size={12} color=\"#9fa2ab\" />\n </Button>\n </Popover.Trigger>\n\n <Popover.Content\n className=\"border-line shadow-box z-50 w-[242px] rounded-xl border bg-white p-4\"\n align=\"center\"\n sideOffset={18}\n >\n <div>\n <div\n className=\"text-content-first border-line mb-[10px] flex h-8 cursor-pointer items-center justify-center rounded-lg border text-sm\"\n onClick={() => {\n editor.chain().focus().unsetBackgroundColor().run();\n }}\n >\n 默认颜色\n </div>\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n {HIGHLIGHT_COLORS.map((color) => (\n <span\n key={color}\n className={cn(\n \"hover:border-content-first flex h-7 w-7 cursor-pointer items-center justify-center rounded-sm border border-transparent\",\n {\n \"border-content-first\": activeHighlightItem === color,\n }\n )}\n onClick={() => {\n editor.chain().focus().unsetBackgroundColor().run();\n editor.chain().focus().setBackgroundColor(color).run();\n }}\n >\n <span\n className=\"border-line h-6 w-6 rounded-sm border\"\n style={{\n backgroundColor: color,\n }}\n />\n </span>\n ))}\n </div>\n </div>\n </Popover.Content>\n </Popover.Root>\n );\n};\n\nexport default BgSelector;\n","\"use client\";\nimport { memo, useState } from \"react\";\n\nimport { Button } from \"../../../components/ui/button\";\nimport { useCurrentEditor } from \"../../../context\";\nimport { Check, ChevronDown } from \"lucide-react\";\nimport { cn } from \"../../../lib/utils\";\nimport { useEditorDerivedState } from \"../../../hooks/useEditorDerivedState\";\nimport { Popover, PopoverTrigger, PopoverContent } from \"../../../components/ui/popover\";\nimport { leftAlign, textAlignActions } from \"../../../toolbarAction\";\n\nconst AlignSelector = () => {\n const { editor } = useCurrentEditor();\n\n const [open, setOpen] = useState(false);\n\n const state = useEditorDerivedState();\n\n const activeItem = textAlignActions.find((item) => item.isActive?.(state)) ?? leftAlign;\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button onClick={() => setOpen(true)} variant=\"ghost\" className=\"h-7 gap-1 rounded px-1\">\n <activeItem.icon strokeWidth={1.5} size=\"20\" />\n <ChevronDown size={12} color=\"#9fa2ab\" />\n </Button>\n </PopoverTrigger>\n\n <PopoverContent\n className=\"border-line z-50 w-40 rounded-xl border bg-white px-2 py-1 shadow-xl\"\n align=\"start\"\n sideOffset={18}\n >\n {textAlignActions.map((item) => {\n return (\n <div\n key={item.id}\n onClick={() => {\n item.onClick(editor);\n setOpen(false);\n }}\n className={cn(\n \"flex cursor-pointer items-center rounded-sm p-2 text-sm hover:bg-accent\",\n activeItem?.label === item.label ? \"text-primary\" : \"\"\n )}\n >\n <item.icon size=\"20\" className=\"mr-3\" />\n <span>{item.label}</span>\n {activeItem?.label === item.label && (\n <Check size=\"16\" className=\"ml-auto items-end\" />\n )}\n </div>\n );\n })}\n </PopoverContent>\n </Popover>\n );\n};\n\nexport default memo(AlignSelector);\n","import { ImagePlus } from \"lucide-react\";\nimport { useCurrentEditor } from \"../../../context\";\nimport { Button } from \"../../../components/ui/button\";\n\nconst InsetImageButton = () => {\n const { editor } = useCurrentEditor();\n\n return (\n <Button\n variant=\"ghost\"\n className=\"aspect-square h-7 gap-1 rounded px-1\"\n onClick={() => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = \"image/*\";\n input.onchange = async (event) => {\n const file = (event.target as HTMLInputElement).files?.[0];\n if (file) {\n editor.commands.setImages([{ src: file }]);\n }\n };\n input.click();\n }}\n >\n <ImagePlus size=\"20\" />\n </Button>\n );\n};\n\nexport default InsetImageButton;\n","export const TABLE_INIT_GRID_SIZE = 10;\nexport const TABLE_MAX_GRID_SIZE = 10;\nexport const TABLE_MAX_ROW_SIZE = 100;\nexport const TABLE_MAX_COLUMN_SIZE = 50;\nexport const TABLE_DEFAULT_SELECTED_GRID_SIZE = 2;\n\nexport const IMAGE_MAX_SIZE = 10;\nexport const IMAGE_MAX_WIDTH = 1000;\nexport const IMAGE_MAX_HEIGHT = 840;\nexport const IMAGE_MIN_HEIGHT = 120;\nexport const IMAGE_MIN_WIDTH = 120;\nexport const IMAGE_THROTTLE_WAIT_TIME = 16;\n","import React from \"react\";\nimport { ToolbarButton } from \"../action-button\";\nimport { Table } from \"lucide-react\";\nimport { useCurrentEditor } from \"../../../context\";\nimport { useEditorDerivedState } from \"../../../hooks/useEditorDerivedState\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../../components/ui/popover\";\nimport { TABLE_DEFAULT_SELECTED_GRID_SIZE, TABLE_INIT_GRID_SIZE } from \"../../../utils/constant\";\n\nconst createArray = (length: number) => Array.from({ length }).map((_, index) => index + 1);\n\ninterface IPropsCreateTablePopover {\n createTable: any;\n children: any;\n}\n\nexport interface GridSize {\n rows: number;\n cols: number;\n}\n\nexport interface CreateTablePayload extends GridSize {\n withHeaderRow: boolean;\n}\n\nfunction TableActionSelector() {\n const { editor } = useCurrentEditor();\n const { canTable, isTable } = useEditorDerivedState();\n\n function createTable(options: any) {\n if (canTable) {\n editor\n .chain()\n .focus()\n .insertTable({ ...options, withHeaderRow: false })\n .run();\n }\n }\n\n const [withHeaderRow, setWithHeaderRow] = React.useState<boolean>(true);\n const [tableGridSize, setTableGridSize] = React.useState<GridSize>({\n rows: TABLE_INIT_GRID_SIZE,\n cols: TABLE_INIT_GRID_SIZE,\n });\n\n const [selectedTableGridSize, setSelectedTableGridSize] = React.useState<GridSize>({\n rows: TABLE_DEFAULT_SELECTED_GRID_SIZE,\n cols: TABLE_DEFAULT_SELECTED_GRID_SIZE,\n });\n\n function selectTableGridSize(rows: number, cols: number): void {\n if (rows === tableGridSize.rows) {\n setTableGridSize((prev) => {\n return {\n ...prev,\n rows: Math.min(rows + 1, TABLE_INIT_GRID_SIZE),\n };\n });\n }\n\n if (cols === tableGridSize.cols) {\n setTableGridSize((prev) => {\n return {\n ...prev,\n cols: Math.min(cols + 1, TABLE_INIT_GRID_SIZE),\n };\n });\n }\n\n setSelectedTableGridSize({\n rows,\n cols,\n });\n }\n\n function onMouseDown(rows: number, cols: number) {\n createTable({ rows, cols, withHeaderRow });\n resetTableGridSize();\n }\n\n function resetTableGridSize(): void {\n setWithHeaderRow(false);\n\n setTableGridSize({\n rows: TABLE_INIT_GRID_SIZE,\n cols: TABLE_INIT_GRID_SIZE,\n });\n\n setSelectedTableGridSize({\n rows: TABLE_DEFAULT_SELECTED_GRID_SIZE,\n cols: TABLE_DEFAULT_SELECTED_GRID_SIZE,\n });\n }\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <ToolbarButton\n data-state=\"off\"\n disabled={!canTable || isTable}\n tooltip=\"插入表格\"\n tooltipOptions={{ side: \"bottom\" }}\n isActive={isTable}\n >\n <Table size={20} />\n </ToolbarButton>\n </PopoverTrigger>\n <PopoverContent className=\"z-50 w-full !p-2\" align=\"start\" side=\"bottom\" sideOffset={18}>\n <div className=\"table-grid-size-editor p-0\">\n <div className=\"flex flex-col flex-wrap justify-between gap-1\">\n {createArray(tableGridSize?.rows)?.map((row: any) => {\n return (\n <div key={`r-${row}`} className=\"flex gap-1\">\n {createArray(tableGridSize?.cols)?.map((col: any) => {\n return (\n <div\n key={`c-${col}`}\n className={`border-line box-border h-4 w-4 cursor-pointer rounded-[2px] border-solid bg-gray-200 ${\n col <= selectedTableGridSize.cols && row <= selectedTableGridSize.rows\n ? \"bg-primary/60\"\n : \"bg-gray-100\"\n }`}\n onMouseOver={() => selectTableGridSize(row, col)}\n onMouseDown={() => onMouseDown(row, col)}\n ></div>\n );\n })}\n </div>\n );\n })}\n </div>\n <div className=\"mt-2 text-center text-sm text-zinc-600\">\n {selectedTableGridSize.rows} x {selectedTableGridSize.cols}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport default TableActionSelector;\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Separator({\n className,\n orientation = \"horizontal\",\n decorative = true,\n ...props\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\n return (\n <SeparatorPrimitive.Root\n data-slot=\"separator\"\n decorative={decorative}\n orientation={orientation}\n className={cn(\n \"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport { Separator };\n","import { useCurrentEditor } from \"../context\";\nimport { IActionItem, redo, textFormatActions, toolbarNodeConfig, undo } from \"../toolbarAction\";\nimport { ToolbarButton } from \"./components/action-button\";\nimport NodeSelector from \"./components/selectors/node-selector\";\nimport TextSelector from \"./components/selectors/text-selector\";\nimport LinkSelector from \"./components/selectors/link-selector\";\nimport ColorSelector from \"./components/selectors/color-selector\";\nimport BgSelector from \"./components/selectors/bg-selector\";\nimport AlignSelector from \"./components/selectors/align-selector\";\nimport { useEditorDerivedState } from \"../hooks/useEditorDerivedState\";\nimport InsetImageSelector from \"./components/selectors/inset-image-selector\";\nimport TableActionSelector from \"./components/selectors/table-action-selector\";\nimport { Separator } from \"../components/ui/separator\";\nimport { type Editor } from \"@tiptap/core\";\nimport React from \"react\";\n\nexport type ToolbarItemKey =\n | \"undo\"\n | \"redo\"\n | \"node-selector\"\n | \"text-selector\"\n | \"link\"\n | \"color\"\n | \"bg\"\n | \"align\"\n | \"image\"\n | \"table\"\n | \"separator\";\n\nexport interface ToolbarConfigItem {\n key: ToolbarItemKey | string; // 支持内置 key 或自定义 key\n render?: (editor: Editor) => React.ReactNode; // 如果是自定义组件,通过 render 函数传入\n}\n\nexport interface ToolbarProps {\n config?: (ToolbarItemKey | ToolbarConfigItem)[]; // 接收 key 数组或对象数组\n appendItems?: React.ReactNode; // 方便在末尾快速追加\n}\n\nconst DEFAULT_CONFIG: ToolbarItemKey[] = [\n \"undo\",\n \"redo\",\n \"separator\",\n \"node-selector\",\n \"text-selector\",\n \"link\",\n \"color\",\n \"bg\",\n \"align\",\n \"separator\",\n \"image\",\n \"table\",\n];\n\nconst Toolbar = ({ config = DEFAULT_CONFIG, appendItems }: ToolbarProps) => {\n const { editor } = useCurrentEditor();\n const state = useEditorDerivedState();\n\n if (!editor) return null;\n\n const getBtnProps = (item: any) => ({\n isActive: item.isActive?.(state) ?? false,\n disabled: item.disabled?.(state) ?? false,\n onClick: () => item.onClick(editor),\n });\n\n const renderItem = (item: ToolbarItemKey | ToolbarConfigItem, index: number) => {\n // 处理字符串类型的配置\n const key = typeof item === \"string\" ? item : item.key;\n\n switch (key) {\n case \"undo\":\n return (\n <ToolbarButton\n key={index}\n {...getBtnProps(undo)}\n tooltip={undo.label}\n shortcutKeys={undo.shortcutKeys}\n >\n <undo.icon size={20} />\n </ToolbarButton>\n );\n case \"redo\":\n return (\n <ToolbarButton\n key={index}\n {...getBtnProps(redo)}\n tooltip={redo.label}\n shortcutKeys={redo.shortcutKeys}\n >\n <redo.icon size={20} />\n </ToolbarButton>\n );\n case \"separator\":\n return <Separator key={index} orientation=\"vertical\" className=\"bg-line h-4\" />;\n case \"node-selector\":\n return <NodeSelector key={index} nodeFormatActions={toolbarNodeConfig.select} />;\n case \"text-selector\":\n return <TextSelector key={index} textFormatActions={textFormatActions} />;\n case \"link\":\n return <LinkSelector key={index} />;\n case \"color\":\n return <ColorSelector key={index} />;\n case \"bg\":\n return <BgSelector key={index} />;\n case \"align\":\n return <AlignSelector key={index} />;\n case \"image\":\n return <InsetImageSelector key={index} />;\n case \"table\":\n return <TableActionSelector key={index} />;\n default:\n // 如果是自定义配置且带有 render 函数\n if (typeof item !== \"string\" && item.render) {\n return <React.Fragment key={index}>{item.render(editor)}</React.Fragment>;\n }\n return null;\n }\n };\n\n return (\n <div className=\"z-99 flex flex-wrap items-center gap-1 border-b bg-white px-4 py-2 shadow-sm\">\n {config.map((item, idx) => renderItem(item, idx))}\n {appendItems}\n </div>\n );\n};\n\nexport { Toolbar };\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\n}\n\nfunction DropdownMenuPortal({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\n return <DropdownMenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />;\n}\n\nfunction DropdownMenuTrigger({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n return <DropdownMenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" {...props} />;\n}\n\nfunction DropdownMenuContent({\n className,\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n \"max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin) z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nfunction DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\n return <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />;\n}\n\nfunction DropdownMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n variant?: \"default\" | \"destructive\";\n}) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"data-[variant=destructive]:*:[svg]:!text-destructive outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[disabled]:opacity-50 data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\n return (\n <DropdownMenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(\n \"outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon className=\"size-4\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n );\n}\n\nfunction DropdownMenuRadioGroup({\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\n return <DropdownMenuPrimitive.RadioGroup data-slot=\"dropdown-menu-radio-group\" {...props} />;\n}\n\nfunction DropdownMenuRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {\n return (\n <DropdownMenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(\n \"outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className\n )}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CircleIcon className=\"size-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n );\n}\n\nfunction DropdownMenuLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean;\n}) {\n return (\n <DropdownMenuPrimitive.Label\n data-slot=\"dropdown-menu-label\"\n data-inset={inset}\n className={cn(\"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\", className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n}) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"outline-hidden flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[inset]:pl-8 data-[state=open]:text-accent-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n className\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\nfunction DropdownMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n return (\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"origin-(--radix-dropdown-menu-content-transform-origin) z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\n className\n )}\n {...props}\n />\n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuPortal,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuLabel,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubTrigger,\n DropdownMenuSubContent,\n};\n","\"use client\";\nimport { useState, useRef } from \"react\";\nimport { NodeViewContent, NodeViewWrapper } from \"@tiptap/react\";\nimport { NodeViewProps } from \"@tiptap/core\";\nimport { Copy, Check, ChevronDown, ChevronRight } from \"lucide-react\";\nimport { Button } from \"../../components/ui/button\";\nimport copyTextToClipboard from \"copy-to-clipboard\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"../../components/ui/dropdown-menu\";\nimport { ActionButton } from \"../../toolbar/components/action-button\";\n\nexport const CodeBlock = ({ language, content, extension, updateAttributes }: any) => {\n const [isCopied, setIsCopied] = useState(false);\n const [expand, setExpand] = useState(true);\n const contentRef = useRef<HTMLDivElement>(null);\n\n const handleCopyClick = () => {\n setIsCopied(true);\n setTimeout(() => {\n setIsCopied(false);\n }, 2000);\n };\n\n const CopyIcon = (props: any) => {\n return isCopied ? <Check color=\"#16a34a\" {...props} /> : <Copy color=\"#777\" {...props} />;\n };\n const ExpandIcon = expand ? ChevronDown : ChevronRight;\n const foldStyle = expand ? {} : { height: 0, overflow: \"hidden\" };\n\n return (\n <div className=\"overflow-hidden rounded-lg bg-muted\">\n <div\n contentEditable={false}\n className=\"bg-gray relative box-border flex h-9 w-full flex-row items-center justify-between px-4 py-1\"\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-6 w-6 rounded-full text-content-second\"\n onClick={() => setExpand(!expand)}\n >\n <ExpandIcon\n size={14}\n color=\"#777\"\n style={{\n verticalAlign: \"-0.125em\",\n }}\n />\n </Button>\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <span className=\"min-w-[50px] cursor-pointer text-sm text-[#999]\">\n {language || \"请选择语言\"}\n </span>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"max-h-96 overflow-y-auto\">\n {extension.options.lowlight.listLanguages().map((lang: any, index: number) => (\n <DropdownMenuItem\n key={index}\n onClick={() => {\n updateAttributes({ language: lang });\n }}\n >\n {lang}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n <div className=\"flex gap-1\">\n <ActionButton\n onClick={() => {\n copyTextToClipboard(content || contentRef.current?.innerText || \"\");\n handleCopyClick();\n }}\n tooltip={isCopied ? \"已复制\" : \"复制\"}\n className=\"h-6 w-6 text-content-second\"\n >\n {/* eslint-disable-next-line react-hooks/static-components */}\n <CopyIcon size={16} />\n </ActionButton>\n </div>\n </div>\n <div style={foldStyle} ref={contentRef}>\n <pre>\n <NodeViewContent<\"code\"> as=\"code\" />\n </pre>\n </div>\n </div>\n );\n};\n\nexport const NodeViewCodeBlock = (props: NodeViewProps) => {\n const { node, extension, updateAttributes } = props;\n const language = node.attrs.language;\n const content = node.textContent;\n\n return (\n <NodeViewWrapper data-drag-handle>\n <CodeBlock\n content={content}\n language={language}\n extension={extension}\n updateAttributes={updateAttributes}\n />\n </NodeViewWrapper>\n );\n};\n","\"use client\";\nimport { useState, useCallback, useEffect } from \"react\";\n\nexport type ResizeDirection = \"tl\" | \"tr\" | \"bl\" | \"br\" | \"top\" | \"right\" | \"bottom\" | \"left\";\nexport type ElementDimensions = { width: number; height: number };\n\ntype HookParams = {\n initialWidth?: number;\n initialHeight?: number;\n contentWidth?: number;\n contentHeight?: number;\n gridInterval: number;\n minWidth: number;\n minHeight: number;\n maxWidth: number;\n onDimensionsChange?: (dimensions: ElementDimensions) => void;\n};\n\nexport function useDragResize({\n initialWidth,\n initialHeight,\n contentWidth,\n contentHeight,\n gridInterval,\n minWidth,\n minHeight,\n maxWidth,\n onDimensionsChange,\n}: HookParams) {\n const [dimensions, updateDimensions] = useState<ElementDimensions>({\n width: Math.max(initialWidth ?? minWidth, minWidth),\n height: Math.max(initialHeight ?? minHeight, minHeight),\n });\n const [boundaryWidth, setBoundaryWidth] = useState(Infinity);\n // 修改为记录x和y坐标\n const [resizeOrigin, setResizeOrigin] = useState({ x: 0, y: 0 });\n const [initialDimensions, setInitialDimensions] = useState(dimensions);\n const [resizeDirection, setResizeDirection] = useState<ResizeDirection | undefined>();\n\n const widthConstraint = useCallback(\n (proposedWidth: number, maxAllowedWidth: number) => {\n const effectiveMinWidth = Math.max(\n minWidth,\n Math.min(contentWidth ?? minWidth, (gridInterval / 100) * maxAllowedWidth)\n );\n return Math.min(maxAllowedWidth, Math.max(proposedWidth, effectiveMinWidth));\n },\n [gridInterval, contentWidth, minWidth]\n );\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n event.preventDefault();\n\n if (!resizeDirection) return;\n\n // 计算水平和垂直方向的移动距离\n let deltaX = 0;\n let deltaY = 0;\n\n // 根据不同方向计算移动距离\n switch (resizeDirection) {\n case \"tl\": // 左上角\n deltaX = resizeOrigin.x - event.pageX;\n deltaY = resizeOrigin.y - event.pageY;\n break;\n case \"tr\": // 右上角\n deltaX = event.pageX - resizeOrigin.x;\n deltaY = resizeOrigin.y - event.pageY;\n break;\n case \"bl\": // 左下角\n deltaX = resizeOrigin.x - event.pageX;\n deltaY = event.pageY - resizeOrigin.y;\n break;\n case \"br\": // 右下角\n deltaX = event.pageX - resizeOrigin.x;\n deltaY = event.pageY - resizeOrigin.y;\n break;\n case \"top\": // 顶部 - 只改变高度\n deltaX = resizeOrigin.x;\n deltaY = resizeOrigin.y - event.pageY;\n break;\n case \"right\": // 右侧 - 只改变宽度\n deltaX = event.pageX - resizeOrigin.x;\n deltaY = resizeOrigin.y;\n break;\n case \"bottom\": // 底部 - 只改变高度\n deltaX = resizeOrigin.x;\n deltaY = event.pageY - resizeOrigin.y;\n break;\n case \"left\": // 左侧 - 只改变宽度\n deltaX = resizeOrigin.x - event.pageX;\n deltaY = resizeOrigin.y;\n break;\n }\n\n // 计算宽高比\n const aspectRatio =\n contentWidth && contentHeight\n ? contentWidth / contentHeight\n : initialDimensions.width / initialDimensions.height;\n\n let newWidth = initialDimensions.width;\n let newHeight = initialDimensions.height;\n\n // 根据调整方向计算新的尺寸\n if ([\"tl\", \"tr\", \"bl\", \"br\"].includes(resizeDirection)) {\n // 对角调整 - 保持宽高比\n const scaleFactor =\n Math.abs(deltaX) > Math.abs(deltaY * aspectRatio)\n ? deltaX / initialDimensions.width\n : deltaY / initialDimensions.height;\n\n newWidth = initialDimensions.width + initialDimensions.width * scaleFactor;\n newHeight = newWidth / aspectRatio;\n } else if ([\"left\", \"right\"].includes(resizeDirection)) {\n // 水平方向调整 - 只改变宽度\n newWidth = initialDimensions.width + deltaX;\n } else if ([\"top\", \"bottom\"].includes(resizeDirection)) {\n // 垂直方向调整 - 只改变高度\n newHeight = initialDimensions.height + deltaY;\n }\n\n // 应用网格对齐\n const gridUnitWidth = (gridInterval / 100) * boundaryWidth;\n\n if ([\"tl\", \"tr\", \"bl\", \"br\"].includes(resizeDirection)) {\n // 对角调整 - 保持宽高比\n newWidth = Math.round(newWidth / gridUnitWidth) * gridUnitWidth;\n newHeight = newWidth / aspectRatio;\n }\n\n // 应用约束\n if ([\"tl\", \"tr\", \"bl\", \"br\", \"left\", \"right\"].includes(resizeDirection)) {\n newWidth = widthConstraint(newWidth, boundaryWidth);\n\n // 只有在对角调整时才根据宽度调整高度\n if ([\"tl\", \"tr\", \"bl\", \"br\"].includes(resizeDirection)) {\n newHeight = newWidth / aspectRatio;\n }\n }\n\n updateDimensions({\n width: Math.max(newWidth, minWidth),\n height: Math.max(newHeight, minHeight),\n });\n },\n [\n widthConstraint,\n resizeDirection,\n boundaryWidth,\n resizeOrigin,\n gridInterval,\n contentHeight,\n contentWidth,\n initialDimensions,\n minWidth,\n minHeight,\n ]\n );\n\n const handlePointerUp = useCallback(\n (event: PointerEvent) => {\n event.preventDefault();\n event.stopPropagation();\n\n setResizeOrigin({ x: 0, y: 0 });\n setResizeDirection(undefined);\n onDimensionsChange?.(dimensions);\n },\n [onDimensionsChange, dimensions]\n );\n\n const handleKeydown = useCallback(\n (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n updateDimensions({\n width: Math.max(initialDimensions.width, minWidth),\n height: Math.max(initialDimensions.height, minHeight),\n });\n setResizeDirection(undefined);\n }\n },\n [initialDimensions, minWidth, minHeight]\n );\n\n const initiateResize = useCallback(\n (direction: ResizeDirection) => (event: React.PointerEvent<HTMLDivElement>) => {\n event.preventDefault();\n event.stopPropagation();\n console.log(\"initiateResize direction\", direction);\n setBoundaryWidth(maxWidth);\n setInitialDimensions({\n width: Math.max(widthConstraint(dimensions.width, maxWidth), minWidth),\n height: Math.max(dimensions.height, minHeight),\n });\n setResizeOrigin({ x: event.pageX, y: event.pageY });\n setResizeDirection(direction);\n },\n [maxWidth, widthConstraint, dimensions.width, dimensions.height, minWidth, minHeight]\n );\n\n useEffect(() => {\n if (resizeDirection) {\n document.addEventListener(\"keydown\", handleKeydown);\n document.addEventListener(\"pointermove\", handlePointerMove);\n document.addEventListener(\"pointerup\", handlePointerUp);\n\n return () => {\n document.removeEventListener(\"keydown\", handleKeydown);\n document.removeEventListener(\"pointermove\", handlePointerMove);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n }\n }, [resizeDirection, handleKeydown, handlePointerMove, handlePointerUp]);\n\n return {\n initiateResize,\n isResizing: !!resizeDirection,\n updateDimensions,\n currentWidth: Math.max(dimensions.width, minWidth),\n currentHeight: Math.max(dimensions.height, minHeight),\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { XIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean;\n}) {\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg outline-none duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 sm:max-w-lg\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"rounded-xs focus:outline-hidden absolute right-4 top-4 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\"\n >\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\"flex flex-col gap-2 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg font-semibold leading-none\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n","\"use client\";\n\nimport { Editor } from \"@tiptap/core\";\nimport React, { useRef } from \"react\";\nimport { Cropper } from \"react-cropper\";\nimport {\n RatioIcon as AspectRatio,\n FlipHorizontal,\n FlipVertical,\n RotateCcwSquare,\n RotateCwSquare,\n Square,\n X,\n Check,\n Maximize,\n RefreshCw,\n} from \"lucide-react\";\n\nimport { Dialog, DialogContent, DialogHeader, DialogTitle } from \"../../../components/ui/dialog\";\nimport { Button } from \"../../../components/ui/button\";\nimport { Separator } from \"../../../components/ui/separator\";\n\ninterface ImageCropperProps {\n editor: Editor;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n imageSrc: string;\n onComplete: (src: string) => void;\n}\n\nexport function ImageCropper({\n editor,\n open,\n onOpenChange,\n imageSrc,\n onComplete,\n}: ImageCropperProps) {\n const cropperRef = useRef<any>(null);\n\n const handleComplete = () => {\n if (cropperRef.current) {\n const cropper = cropperRef.current?.cropper;\n if (cropper) {\n cropper.getCroppedCanvas().toBlob(async (blob: Blob | null) => {\n if (!blob) {\n return;\n }\n\n const file = new File([blob], \"cropped-image.jpg\", {\n type: \"image/jpeg\",\n });\n\n const imageExtension = editor?.extensionManager?.extensions?.find(\n (extension: any) => extension.name === \"image\"\n );\n\n // Use uploadFn if available\n if (imageExtension?.options?.uploadFn) {\n try {\n const uploadedSrc = await imageExtension.options.uploadFn(file);\n onComplete(uploadedSrc);\n onOpenChange(false);\n } catch (error) {\n console.error(\"Image upload failed:\", error);\n }\n } else {\n }\n }, \"image/jpeg\");\n }\n }\n };\n\n const handleAspectRatioChange = (ratio: number | undefined) => {\n if (cropperRef.current?.cropper) {\n cropperRef.current.cropper.setAspectRatio(ratio || NaN);\n }\n };\n\n const handleRotate = (degree: number) => {\n if (cropperRef.current?.cropper) {\n cropperRef.current.cropper.rotate(degree);\n }\n };\n\n const handleFlip = (horizontal: boolean, vertical: boolean) => {\n if (cropperRef.current?.cropper) {\n const cropper = cropperRef.current.cropper;\n if (horizontal) {\n cropper.scaleX(-cropper.getData().scaleX || -1);\n }\n if (vertical) {\n cropper.scaleY(-cropper.getData().scaleY || -1);\n }\n }\n };\n\n const handleReset = () => {\n if (cropperRef.current?.cropper) {\n cropperRef.current.cropper.reset();\n handleAspectRatioChange(undefined);\n }\n };\n\n const buttonConfigs = [\n {\n type: \"aspectRatio\",\n buttons: [\n {\n icon: Maximize,\n label: \"自由\",\n action: () => handleAspectRatioChange(undefined),\n },\n {\n icon: Square,\n label: \"1:1\",\n action: () => handleAspectRatioChange(1),\n },\n {\n icon: AspectRatio,\n label: \"3:2\",\n action: () => handleAspectRatioChange(3 / 2),\n },\n {\n icon: AspectRatio,\n label: \"16:9\",\n action: () => handleAspectRatioChange(16 / 9),\n },\n ],\n },\n {\n type: \"flip\",\n buttons: [\n {\n icon: FlipVertical,\n label: \"垂直翻转\",\n action: () => handleFlip(false, true),\n },\n {\n icon: FlipHorizontal,\n label: \"水平翻转\",\n action: () => handleFlip(true, false),\n },\n ],\n },\n {\n type: \"rotate\",\n buttons: [\n {\n icon: RotateCcwSquare,\n label: \"向左90°\",\n action: () => handleRotate(-90),\n },\n {\n icon: RotateCwSquare,\n label: \"向右90°\",\n action: () => handleRotate(90),\n },\n ],\n },\n {\n type: \"actions\",\n buttons: [\n {\n icon: RefreshCw,\n label: \"重置\",\n action: handleReset,\n },\n {\n icon: X,\n label: \"放弃\",\n action: () => onOpenChange(false),\n },\n {\n icon: Check,\n label: \"完成\",\n action: handleComplete,\n // variant: 'default'\n },\n ],\n },\n ];\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent\n showCloseButton={false}\n className=\"flex h-[720px] max-h-[90vh] max-w-screen-lg flex-col gap-0 px-8 pb-0 pt-8\"\n >\n <DialogHeader className=\"hidden\">\n <DialogTitle>编辑图片</DialogTitle>\n </DialogHeader>\n <div className=\"relative w-full flex-grow overflow-hidden rounded-md\">\n <Cropper\n ref={cropperRef}\n src={imageSrc}\n style={{ height: \"100%\", width: \"100%\" }}\n initialAspectRatio={undefined}\n viewMode={0}\n dragMode=\"move\"\n autoCropArea={1}\n />\n </div>\n <div className=\"flex h-20 items-center justify-center gap-2\">\n {buttonConfigs.map((group, groupIndex) => (\n <React.Fragment key={group.type}>\n {group.buttons.map(({ icon: Icon, label, action, variant = \"ghost\" }: any) => (\n <Button\n // size=\"sm\"\n key={label}\n variant={variant as any}\n className=\"flex h-14 w-14 flex-col items-center justify-center gap-1 hover:bg-transparent hover:text-primary\"\n onClick={action}\n >\n <Icon size={20} />\n <span className=\"text-xs\">{label}</span>\n </Button>\n ))}\n {groupIndex < buttonConfigs.length - 1 && (\n <Separator orientation=\"vertical\" className=\"h-12\" />\n )}\n </React.Fragment>\n ))}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n","import * as React from \"react\";\nimport { AlignLeft, AlignCenter, AlignRight, Crop, Trash2, Maximize } from \"lucide-react\";\n\nimport { cn } from \"../../../lib/utils\";\nimport { Separator } from \"../../../components/ui/separator\";\nimport { ImageCropper } from \"./image-cropper\";\nimport { ActionButton } from \"../../../toolbar/components/action-button\";\n\nexport const ActionWrapper = React.memo(\n React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ children, className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"absolute -top-14 left-0 opacity-0 group-hover/node-image:opacity-100\",\n \"border-line z-50 flex items-center gap-3 rounded-xl border bg-white px-3 py-2 shadow-[0px_0px_20px_0px_rgba(0,0,0,0.1)]\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n )\n )\n);\n\nActionWrapper.displayName = \"ActionWrapper\";\n\nexport const ImageActions: React.FC<any> = React.memo(\n ({ editor, node, imageState, setImageState, updateAttributes }) => {\n const [openEdit, setOpenEdit] = React.useState(false);\n\n const onComplete = (src: string) => {\n setImageState((prev: any) => ({\n ...prev,\n src,\n isServerUploading: false,\n }));\n updateAttributes({ src });\n editor.commands.setImages([{ src }]);\n editor.chain().focus().run();\n };\n\n const alignButtons = [\n {\n icon: AlignLeft,\n tooltip: \"左对齐\",\n onClick: () => updateAttributes({ align: \"left\" }),\n },\n {\n icon: AlignCenter,\n tooltip: \"居中对齐\",\n onClick: () => updateAttributes({ align: \"center\" }),\n },\n {\n icon: AlignRight,\n tooltip: \"右对齐\",\n onClick: () => updateAttributes({ align: \"right\" }),\n },\n ];\n\n const actionButtons = [\n {\n icon: Maximize,\n tooltip: \"预览\",\n onClick: () => {\n setImageState((prev: any) => ({ ...prev, isZoomed: true }));\n },\n },\n // {\n // icon: Crop,\n // tooltip: \"编辑\",\n // onClick: () => {\n // setOpenEdit(true);\n // },\n // },\n {\n icon: Trash2,\n tooltip: \"移除\",\n onClick: () => {\n editor.commands.command(({ tr, dispatch }: any) => {\n const { selection } = tr;\n const nodeAtSelection = tr.doc.nodeAt(selection.from);\n\n if (nodeAtSelection && nodeAtSelection.type.name === \"image\") {\n if (dispatch) {\n tr.deleteSelection();\n return true;\n }\n }\n return false;\n });\n },\n },\n ];\n\n return (\n <ActionWrapper>\n {alignButtons.map(({ icon: Icon, tooltip, onClick }, index) => (\n <ActionButton key={index} tooltip={tooltip} onClick={onClick}>\n <Icon size=\"20\" />\n </ActionButton>\n ))}\n <Separator className=\"h-5\" orientation=\"vertical\" />\n {actionButtons.map(({ icon: Icon, tooltip, onClick }, index) => (\n <ActionButton key={index} tooltip={tooltip} onClick={onClick}>\n <Icon size=\"20\" />\n </ActionButton>\n ))}\n <ImageCropper\n editor={editor}\n onOpenChange={setOpenEdit}\n open={openEdit}\n onComplete={onComplete}\n imageSrc={imageState.src}\n />\n </ActionWrapper>\n );\n }\n);\n\nImageActions.displayName = \"ImageActions\";\n","import * as React from \"react\";\nimport { LoaderCircle } from \"lucide-react\";\nimport { cn } from \"../../../lib/utils\";\n\nexport const ImageOverlay = React.memo(() => {\n return (\n <div\n className={cn(\n \"flex flex-row items-center justify-center\",\n \"absolute inset-0 rounded bg-[var(--mt-overlay)] opacity-100 transition-opacity\"\n )}\n >\n <LoaderCircle className=\"size-7 animate-spin\" />\n </div>\n );\n});\n\nImageOverlay.displayName = \"ImageOverlay\";\n","import * as React from \"react\";\nimport { cn } from \"../../../lib/utils\";\n\n// 添加四个方向\nconst resizeDirections = [\"tl\", \"tr\", \"bl\", \"br\"];\n// const resizeDirections = [\"tl\", \"tr\", \"bl\", \"br\", \"top\", \"right\", \"bottom\", \"left\"];\n\nexport const ResizeHandle = ({ className, initiateResize }: any) => {\n // 根据方向确定样式\n const directionStyles = React.useCallback((direction: any) => {\n const commonStyles = \"absolute z-10 block border border-primary bg-white\";\n\n let directionStyles = \"\";\n switch (direction) {\n case \"tl\":\n directionStyles =\n \"top-0 left-0 -translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-nwse-resize\";\n break;\n case \"tr\":\n directionStyles =\n \"top-0 right-0 translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-nesw-resize\";\n break;\n case \"bl\":\n directionStyles =\n \"bottom-0 left-0 -translate-x-1/2 translate-y-1/2 h-3 w-3 cursor-nesw-resize\";\n break;\n case \"br\":\n directionStyles =\n \"bottom-0 right-0 translate-x-1/2 translate-y-1/2 h-3 w-3 cursor-nwse-resize\";\n break;\n case \"top\":\n directionStyles =\n \"top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-ns-resize\";\n break;\n case \"right\":\n directionStyles =\n \"top-1/2 right-0 translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-ew-resize\";\n break;\n case \"bottom\":\n directionStyles =\n \"bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 h-3 w-3 cursor-ns-resize\";\n break;\n case \"left\":\n directionStyles =\n \"top-1/2 left-0 -translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-ew-resize\";\n break;\n default:\n }\n\n return cn(commonStyles, directionStyles);\n }, []);\n\n return (\n <div\n className={cn(\n \"absolute left-0 top-0 z-10 h-full w-full cursor-pointer border border-primary\",\n className\n )}\n >\n {resizeDirections?.map((direction) => {\n return (\n <span\n key={direction}\n className={directionStyles(direction)}\n onPointerDown={(event) => initiateResize(direction)(event)}\n ></span>\n );\n })}\n </div>\n );\n};\n\nResizeHandle.displayName = \"ResizeHandle\";\n","export type FileError = {\n file: File | string;\n reason: \"type\" | \"size\" | \"invalidBase64\" | \"base64NotAllowed\";\n};\n\nexport type FileValidationOptions = {\n allowedMimeTypes: string[];\n maxFileSize?: number;\n allowBase64: boolean;\n};\n\ntype FileInput = File | { src: string | File; alt?: string; title?: string };\n\nfunction detectImageFormat(buffer: Buffer) {\n // 检查文件头部的魔数\n const signatures = {\n // JPEG/JPG 的魔数\n \"image/jpeg\": [[0xff, 0xd8, 0xff]],\n\n // PNG 的魔数\n \"image/png\": [[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]],\n\n // GIF 的魔数\n \"image/gif\": [\n [0x47, 0x49, 0x46, 0x38, 0x37, 0x61], // GIF87a\n [0x47, 0x49, 0x46, 0x38, 0x39, 0x61], // GIF89a\n ],\n\n // WebP 的魔数\n \"image/webp\": [[0x52, 0x49, 0x46, 0x46, null, null, null, null, 0x57, 0x45, 0x42, 0x50]],\n\n // BMP 的魔数\n \"image/bmp\": [[0x42, 0x4d]],\n\n // TIFF 的魔数\n \"image/tiff\": [\n [0x49, 0x49, 0x2a, 0x00], // Little-endian\n [0x4d, 0x4d, 0x00, 0x2a], // Big-endian\n ],\n };\n\n // 检查每种图片格式的签名\n for (const [mimeType, signatureList] of Object.entries(signatures)) {\n for (const signature of signatureList) {\n let matches = true;\n\n for (let i = 0; i < signature.length; i++) {\n // 跳过 null 值(用于可变字节)\n if (signature[i] === null) continue;\n\n if (buffer[i] !== signature[i]) {\n matches = false;\n break;\n }\n }\n\n if (matches) {\n return mimeType;\n }\n }\n }\n\n return \"unknown\";\n}\n\nexport const base64ToFile = (base64String: string) => {\n // 判断是不是base64字符串\n if (!base64String || !base64String.startsWith(\"data:\") || !base64String.includes(\";base64,\")) {\n return null;\n }\n\n const [base64Pre, base64Data] = base64String.split(\",\");\n\n let mimeType = base64Pre.replace(\"data:\", \"\").replace(\";base64\", \"\");\n const ext = mimeType.split(\"/\")[1];\n const filename = Math.random().toString(36).substr(2, 9) + \".\" + ext;\n\n // 将 base64 解码为二进制数据\n const binaryData = Buffer.from(base64Data, \"base64\");\n\n if (!mimeType.startsWith(\"image/\")) {\n mimeType = detectImageFormat(binaryData);\n }\n // 创建一个 Blob 对象\n const blob = new Blob([binaryData], { type: mimeType });\n\n // 创建一个 File 对象\n return new File([blob], filename, { type: mimeType });\n};\n\nexport const blobUrlToBase64 = async (blobUrl: string): Promise<string> => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n if (typeof reader.result === \"string\") {\n resolve(reader.result);\n } else {\n reject(new Error(\"Failed to convert Blob to base64\"));\n }\n };\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n};\n\nconst base64MimeType = (encoded: string): string => {\n const result = encoded.match(/data:([a-zA-Z0-9]+\\/[a-zA-Z0-9-.+]+).*,.*/);\n return result && result.length > 1 ? result[1] : \"unknown\";\n};\n\nconst isBase64 = (str: string): boolean => {\n if (str.startsWith(\"data:\")) {\n const matches = str.match(/^data:[^;]+;base64,(.+)$/);\n if (matches && matches[1]) {\n str = matches[1];\n } else {\n return false;\n }\n }\n\n try {\n return btoa(atob(str)) === str;\n } catch {\n return false;\n }\n};\n\nconst checkTypeAndSize = (\n input: File | string,\n { allowedMimeTypes, maxFileSize }: FileValidationOptions\n): { isValidType: boolean; isValidSize: boolean } => {\n const mimeType = input instanceof File ? input.type : base64MimeType(input);\n const size = input instanceof File ? input.size : atob(input.split(\",\")[1]).length;\n\n const isValidType =\n allowedMimeTypes.length === 0 ||\n allowedMimeTypes.includes(mimeType) ||\n allowedMimeTypes.includes(`${mimeType.split(\"/\")[0]}/*`);\n\n const isValidSize = !maxFileSize || size <= maxFileSize;\n\n return { isValidType, isValidSize };\n};\n\nconst validateFileOrBase64 = <T extends FileInput>(\n input: File | string,\n options: FileValidationOptions,\n originalFile: T,\n validFiles: T[],\n errors: FileError[]\n): void => {\n const { isValidType, isValidSize } = checkTypeAndSize(input, options);\n\n if (isValidType && isValidSize) {\n validFiles.push(originalFile);\n } else {\n if (!isValidType) errors.push({ file: input, reason: \"type\" });\n if (!isValidSize) errors.push({ file: input, reason: \"size\" });\n }\n};\n\nexport const isUrl = (\n text: string,\n options: { requireHostname: boolean; allowBase64?: boolean } = {\n requireHostname: false,\n }\n): boolean => {\n if (text.includes(\"\\n\")) return false;\n\n try {\n const url = new URL(text);\n const blockedProtocols = [\n \"javascript:\",\n \"file:\",\n \"vbscript:\",\n ...(options.allowBase64 ? [] : [\"data:\"]),\n ];\n\n if (blockedProtocols.includes(url.protocol)) return false;\n if (options.allowBase64 && url.protocol === \"data:\")\n return /^data:image\\/[a-z]+;base64,/.test(text);\n if (url.hostname) return true;\n\n return (\n url.protocol !== \"\" &&\n (url.pathname.startsWith(\"//\") || url.pathname.startsWith(\"http\")) &&\n !options.requireHostname\n );\n } catch {\n return false;\n }\n};\n\nexport const sanitizeUrl = (\n url: string | null | undefined,\n options: { allowBase64?: boolean } = {}\n): string | undefined => {\n if (!url) return undefined;\n\n if (options.allowBase64 && url.startsWith(\"data:image\")) {\n return isUrl(url, { requireHostname: false, allowBase64: true }) ? url : undefined;\n }\n\n return isUrl(url, {\n requireHostname: false,\n allowBase64: options.allowBase64,\n }) || /^(\\/|#|mailto:|sms:|fax:|tel:)/.test(url)\n ? url\n : `https://${url}`;\n};\n\nexport const filterFiles = <T extends FileInput>(\n files: T[],\n options: FileValidationOptions\n): [T[], FileError[]] => {\n const validFiles: T[] = [];\n const errors: FileError[] = [];\n\n files.forEach((file) => {\n const actualFile = \"src\" in file ? file.src : file;\n\n if (actualFile instanceof File) {\n validateFileOrBase64(actualFile, options, file, validFiles, errors);\n } else if (typeof actualFile === \"string\") {\n if (isBase64(actualFile)) {\n if (options.allowBase64) {\n validateFileOrBase64(actualFile, options, file, validFiles, errors);\n } else {\n errors.push({ file: actualFile, reason: \"base64NotAllowed\" });\n }\n } else {\n if (!sanitizeUrl(actualFile, { allowBase64: options.allowBase64 })) {\n errors.push({ file: actualFile, reason: \"invalidBase64\" });\n } else {\n validFiles.push(file);\n }\n }\n }\n });\n\n return [validFiles, errors];\n};\n\n// 插入到非title节点的光标位置或者文档末尾\nexport const getSafeInsertPosition = (state: any) => {\n if (!state) return;\n const { doc, selection } = state;\n\n const { from, to } = selection;\n\n let safeFrom = from;\n let safeTo = to;\n\n if (from === 0 || doc.nodeAt(from)?.type.name === doc.firstChild?.type.name) {\n const secondNodeStart = doc.content.child(0)?.nodeSize\n ? doc.content.child(0).nodeSize\n : doc.nodeSize;\n safeFrom = secondNodeStart;\n safeTo = secondNodeStart;\n }\n\n const insertPosition = safeTo || safeFrom;\n\n return insertPosition;\n};\n","import * as React from \"react\";\nimport { NodeViewWrapper, type NodeViewProps } from \"@tiptap/react\";\nimport { Trash2, LoaderCircle } from \"lucide-react\";\nimport { Controlled as ControlledZoom } from \"react-medium-image-zoom\";\nimport { type ElementDimensions, useDragResize } from \"../hooks/use-drag-resize\";\nimport { ActionWrapper, ImageActions } from \"./image-actions\";\nimport { ImageOverlay } from \"./image-overlay\";\nimport { ResizeHandle } from \"./resize-handle\";\nimport \"react-medium-image-zoom/dist/styles.css\";\nimport { IMAGE_MAX_HEIGHT, IMAGE_MIN_HEIGHT, IMAGE_MIN_WIDTH } from \"../../../utils/constant\";\nimport { blobUrlToBase64 } from \"../utils\";\nimport { ActionButton } from \"../../../toolbar/components/action-button\";\nimport { cn } from \"../../../lib/utils\";\n\ninterface ImageState {\n src: string;\n isServerUploading: boolean;\n imageLoaded: boolean;\n isZoomed: boolean;\n // angle: number;\n error: boolean;\n naturalSize: ElementDimensions;\n}\n\nexport const ImageViewBlock: React.FC<NodeViewProps> = ({\n editor,\n node,\n selected,\n updateAttributes,\n}) => {\n const {\n src: initialSrc,\n width: initialWidth,\n height: initialHeight,\n align,\n fileName,\n fileType,\n } = node.attrs;\n const [imageState, setImageState] = React.useState<ImageState>({\n src: initialSrc,\n isServerUploading: false,\n imageLoaded: false,\n isZoomed: false,\n // angle: 0,\n error: false,\n naturalSize: { width: initialWidth, height: initialHeight },\n });\n const containerRef = React.useRef<HTMLDivElement>(null);\n\n const onDimensionsChange = React.useCallback(\n ({ width, height }: ElementDimensions) => {\n updateAttributes({ width, height });\n },\n [updateAttributes]\n );\n\n const onRemoveImg = () => {\n editor.commands.command(({ tr, dispatch }: any) => {\n const { selection } = tr;\n const nodeAtSelection = tr.doc.nodeAt(selection.from);\n\n if (nodeAtSelection && nodeAtSelection.type.name === \"image\") {\n if (dispatch) {\n tr.deleteSelection();\n return true;\n }\n }\n return false;\n });\n };\n\n const aspectRatio = imageState.naturalSize.width / imageState.naturalSize.height;\n const maxWidth = IMAGE_MAX_HEIGHT * aspectRatio;\n const containerMaxWidth = containerRef.current\n ? parseFloat(getComputedStyle(containerRef.current).getPropertyValue(\"--editor-width\"))\n : Infinity;\n\n const { currentWidth, currentHeight, updateDimensions, initiateResize, isResizing } =\n useDragResize({\n initialWidth: initialWidth ?? imageState.naturalSize.width,\n initialHeight: initialHeight ?? imageState.naturalSize.height,\n contentWidth: imageState.naturalSize.width,\n contentHeight: imageState.naturalSize.height,\n gridInterval: 0.1,\n onDimensionsChange,\n minWidth: IMAGE_MIN_WIDTH,\n minHeight: IMAGE_MIN_HEIGHT,\n maxWidth: containerMaxWidth > 0 ? containerMaxWidth : maxWidth,\n });\n\n const handleImageLoad = React.useCallback(\n (ev: React.SyntheticEvent<HTMLImageElement>) => {\n const img = ev.target as HTMLImageElement;\n const newNaturalSize = {\n width: img.naturalWidth,\n height: img.naturalHeight,\n };\n setImageState((prev) => ({\n ...prev,\n naturalSize: newNaturalSize,\n imageLoaded: true,\n }));\n updateAttributes({\n width: img.width || newNaturalSize.width,\n height: img.height || newNaturalSize.height,\n alt: img.alt,\n title: img.title,\n });\n\n if (!initialWidth) {\n updateDimensions((state) => ({\n ...state,\n width: newNaturalSize.width,\n }));\n }\n },\n [initialWidth, updateAttributes, updateDimensions]\n );\n\n const handleImageError = React.useCallback(() => {\n setImageState((prev) => ({ ...prev, error: true, imageLoaded: true }));\n }, []);\n\n const handleImage = async () => {\n const imageExtension = editor.options.extensions.find((ext) => ext.name === \"image\");\n const { uploadFn } = imageExtension?.options ?? {};\n\n // 检查是否为blob URL或非taichu-public.wair.ac.cn域名的URL\n if (\n initialSrc.startsWith(\"blob:\") ||\n (initialSrc.startsWith(\"http\") && initialSrc.includes(\"pixabay.com\"))\n ) {\n if (imageState.isServerUploading) return;\n\n if (!uploadFn) {\n try {\n const base64 = await blobUrlToBase64(initialSrc);\n setImageState((prev) => ({ ...prev, src: base64 }));\n updateAttributes({ src: base64 });\n } catch {\n setImageState((prev) => ({ ...prev, error: true }));\n }\n } else {\n try {\n setImageState((prev) => ({ ...prev, isServerUploading: true }));\n\n let blob;\n if (initialSrc.startsWith(\"blob:\")) {\n const response = await fetch(initialSrc);\n blob = await response.blob();\n } else {\n // 处理非taichu-public.wair.ac.cn域名的URL\n const response = await fetch(initialSrc, { mode: \"cors\" });\n blob = await response.blob();\n }\n\n const file = new File([blob], `${fileName || \"image\"}.jpg`, {\n type: fileType || blob.type,\n });\n\n const url = await uploadFn(file);\n setImageState((prev) => ({\n ...prev,\n src: url,\n isServerUploading: false,\n }));\n updateAttributes({ src: url });\n } catch (error) {\n console.error(\"图片上传失败:\", error);\n setImageState((prev) => ({\n ...prev,\n error: true,\n isServerUploading: false,\n }));\n }\n }\n\n if (initialSrc.startsWith(\"blob:\")) {\n URL.revokeObjectURL(initialSrc);\n }\n }\n };\n\n React.useEffect(() => {\n handleImage();\n }, [initialSrc]);\n\n return (\n <NodeViewWrapper\n ref={containerRef}\n data-drag-handle\n className=\"relative flex justify-center\"\n style={{ justifyContent: align }}\n >\n <div\n className=\"node-image group/node-image relative rounded-md object-contain\"\n style={{\n maxWidth: `min(${maxWidth}px, 100%)`,\n width: currentWidth,\n // height: currentHeight,\n maxHeight: IMAGE_MAX_HEIGHT,\n aspectRatio: `${imageState.naturalSize.width} / ${imageState.naturalSize.height}`,\n }}\n >\n <div\n className={cn(\"relative flex h-full cursor-default flex-col items-center gap-2 rounded\")}\n >\n <div className=\"relative h-full\">\n {!imageState.imageLoaded && !imageState.error && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <LoaderCircle className=\"size-7 animate-spin\" />\n </div>\n )}\n\n <ControlledZoom\n isZoomed={imageState.isZoomed}\n onZoomChange={() => setImageState((prev) => ({ ...prev, isZoomed: false }))}\n >\n <img\n className={cn(\"h-auto rounded transition-shadow\", {\n \"opacity-30\": !imageState.imageLoaded || imageState.error,\n })}\n style={{\n // height: currentHeight,\n maxWidth: `min(100%, ${maxWidth}px)`,\n minWidth: `${IMAGE_MIN_WIDTH}px`,\n maxHeight: IMAGE_MAX_HEIGHT,\n cursor: \"pointer\",\n }}\n onDoubleClick={() => {\n setImageState((prev) => ({ ...prev, isZoomed: true }));\n }}\n width={currentWidth}\n height={currentHeight}\n src={imageState.src}\n onError={handleImageError}\n onLoad={handleImageLoad}\n alt={node.attrs.alt || \"\"}\n />\n </ControlledZoom>\n </div>\n\n {imageState.isServerUploading && <ImageOverlay />}\n {editor.isEditable &&\n imageState.imageLoaded &&\n !imageState.error &&\n !imageState.isServerUploading &&\n (selected || isResizing) && <ResizeHandle initiateResize={initiateResize} />}\n\n {imageState.error && (\n <ActionWrapper>\n <ActionButton tooltip=\"移除图片\" onClick={onRemoveImg}>\n <Trash2 className=\"size-4\" />\n </ActionButton>\n </ActionWrapper>\n )}\n\n {!isResizing && !imageState.error && !imageState.isServerUploading && (\n <ImageActions\n editor={editor}\n node={node}\n imageState={imageState}\n setImageState={setImageState}\n updateAttributes={updateAttributes}\n />\n )}\n </div>\n </div>\n </NodeViewWrapper>\n );\n};\n","import { Image as TiptapImage, type ImageOptions } from \"@tiptap/extension-image\";\nimport { ReactNodeViewRenderer, type Editor } from \"@tiptap/react\";\nimport { ImageViewBlock } from \"./widget/image-view-block\";\nimport { FileError, FileValidationOptions, filterFiles, getSafeInsertPosition } from \"./utils\";\n\ntype ImageAction = \"download\" | \"copyImage\" | \"copyLink\";\n\ninterface DownloadImageCommandProps {\n src: string;\n alt?: string;\n}\n\ninterface ImageActionProps extends DownloadImageCommandProps {\n action: ImageAction;\n}\n\nexport interface CustomImageOptions\n extends ImageOptions, Omit<FileValidationOptions, \"allowBase64\"> {\n uploadFn?: (file: File, editor: Editor) => Promise<string>;\n onActionSuccess?: (props: ImageActionProps) => void;\n onActionError?: (error: Error, props: ImageActionProps) => void;\n customDownloadImage?: (props: ImageActionProps, options: CustomImageOptions) => Promise<void>;\n customCopyImage?: (props: ImageActionProps, options: CustomImageOptions) => Promise<void>;\n customCopyLink?: (props: ImageActionProps, options: CustomImageOptions) => Promise<void>;\n onValidationError?: (errors: FileError[]) => void;\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n setImages: {\n setImages: (attrs: { src: string | File; alt?: string; title?: string }[]) => ReturnType;\n };\n setImageInline: {\n setImageInline: (options: any) => ReturnType;\n };\n updateImage: {\n updateImage: (options: any) => ReturnType;\n };\n setAlignImage: {\n setAlignImage: (align: string) => ReturnType;\n };\n downloadImage: {\n downloadImage: (attrs: DownloadImageCommandProps) => ReturnType;\n };\n copyImage: {\n copyImage: (attrs: DownloadImageCommandProps) => ReturnType;\n };\n copyLink: {\n copyLink: (attrs: DownloadImageCommandProps) => ReturnType;\n };\n }\n}\n\nconst handleError = (\n error: unknown,\n props: ImageActionProps,\n errorHandler?: (error: Error, props: ImageActionProps) => void\n): void => {\n const typedError = error instanceof Error ? error : new Error(\"Unknown error\");\n errorHandler?.(typedError, props);\n};\n\nconst defaultCopyImage = async (\n props: ImageActionProps,\n options: CustomImageOptions\n): Promise<void> => {\n const { src } = props;\n try {\n const res = await fetch(src);\n const blob = await res.blob();\n await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);\n options.onActionSuccess?.({ ...props, action: \"copyImage\" });\n } catch (error) {\n handleError(error, { ...props, action: \"copyImage\" }, options.onActionError);\n }\n};\n\nconst defaultCopyLink = async (\n props: ImageActionProps,\n options: CustomImageOptions\n): Promise<void> => {\n const { src } = props;\n try {\n await navigator.clipboard.writeText(src);\n options.onActionSuccess?.({ ...props, action: \"copyLink\" });\n } catch (error) {\n handleError(error, { ...props, action: \"copyLink\" }, options.onActionError);\n }\n};\n\nconst Image = TiptapImage.extend<CustomImageOptions>({\n atom: true,\n\n addOptions() {\n return {\n ...this.parent?.(),\n allowedMimeTypes: [],\n maxFileSize: 0,\n uploadFn: undefined,\n } as any;\n },\n\n addAttributes() {\n return {\n ...this.parent?.(),\n width: {\n default: undefined,\n },\n height: {\n default: undefined,\n },\n align: {\n default: \"center\",\n parseHTML: (element) => element.getAttribute(\"align\"),\n renderHTML: (attributes) => {\n return {\n align: attributes.align,\n };\n },\n },\n fileName: {\n default: undefined,\n },\n fileType: {\n default: undefined,\n },\n };\n },\n\n addCommands() {\n return {\n setImages:\n (attrs) =>\n ({ commands, editor }) => {\n const [validImages, errors] = filterFiles(attrs, {\n allowedMimeTypes: this.options.allowedMimeTypes,\n maxFileSize: this.options.maxFileSize,\n allowBase64: this.options.allowBase64,\n });\n\n if (errors.length > 0 && this.options.onValidationError) {\n this.options.onValidationError(errors);\n }\n\n if (validImages.length > 0) {\n validImages.forEach(async (image) => {\n if (image.src instanceof File) {\n const file = image.src;\n const localSrc = URL.createObjectURL(file);\n\n // 先插占位图\n commands.insertContent({\n type: this.name,\n attrs: { src: localSrc, fileName: file.name },\n });\n\n // 自动触发上传逻辑\n if (this.options.uploadFn) {\n const remoteSrc = await this.options.uploadFn(file, editor);\n // 注意:这里需要精准找到刚才插入的节点并更新,\n // 简单处理可以使用 updateAttributes\n editor.commands.updateAttributes(this.name, { src: remoteSrc });\n }\n } else {\n // 普通 URL 直接插入\n commands.insertContent({\n type: this.name,\n attrs: { src: image.src },\n });\n }\n });\n (validImages.map((image) => {\n if (image.src instanceof File) {\n const blobUrl = URL.createObjectURL(image.src);\n return {\n type: this.type.name,\n attrs: {\n src: blobUrl,\n alt: image.alt,\n title: image.title,\n fileName: image.src.name,\n fileType: image.src.type,\n width: 400,\n },\n };\n } else {\n return {\n type: this.type.name,\n attrs: {\n src: image.src,\n alt: image.alt,\n title: image.title,\n fileName: null,\n fileType: null,\n width: 400,\n },\n };\n }\n }),\n {\n updateSelection: true,\n insertAt: (tr: any, content: any) => {\n const insertPosition = getSafeInsertPosition(tr);\n tr.insertContentAt(content, insertPosition);\n },\n } as any);\n }\n\n return false;\n },\n setImageInline:\n (options: any) =>\n ({ commands }: any) => {\n return commands.insertContent({\n type: this.name,\n attrs: options,\n });\n },\n updateImage:\n (options) =>\n ({ commands }) => {\n console.log(\"updateImage options: \", options);\n return commands.updateAttributes(this.name, options);\n },\n setAlignImage:\n (align) =>\n ({ commands }) => {\n return commands.updateAttributes(this.name, { align });\n },\n copyImage: (attrs) => () => {\n const copyImageFunc = this.options.customCopyImage || defaultCopyImage;\n void copyImageFunc({ ...attrs, action: \"copyImage\" }, this.options);\n return true;\n },\n copyLink: (attrs) => () => {\n const copyLinkFunc = this.options.customCopyLink || defaultCopyLink;\n void copyLinkFunc({ ...attrs, action: \"copyLink\" }, this.options);\n return true;\n },\n };\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(ImageViewBlock, {\n className: \"block-node\",\n });\n },\n});\n\nexport default Image;\n","import { mergeAttributes } from \"@tiptap/core\";\nimport TiptapLink from \"@tiptap/extension-link\";\nimport { getMarkRange } from \"@tiptap/core\";\nimport { Plugin, TextSelection } from \"@tiptap/pm/state\";\n\nexport const Link = TiptapLink.extend({\n inclusive: false,\n parseHTML() {\n return [\n {\n tag: 'a[href]:not([data-type=\"button\"]):not([href *= \"javascript:\" i])',\n },\n ];\n },\n renderHTML({ HTMLAttributes }) {\n return [\n \"a\",\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n class: \"link\",\n }),\n 0,\n ];\n },\n addProseMirrorPlugins() {\n return [\n ...(this.parent?.() || []),\n new Plugin({\n props: {\n handleClick(view, pos) {\n const { schema, doc, tr } = view.state;\n const range = getMarkRange(doc.resolve(pos), schema.marks.link);\n\n if (!range) {\n return;\n }\n\n const { from, to } = range;\n const start = Math.min(from, to);\n const end = Math.max(from, to);\n\n if (pos < start || pos > end) {\n return;\n }\n\n const $start = doc.resolve(start);\n const $end = doc.resolve(end);\n const transaction = tr.setSelection(new TextSelection($start, $end));\n\n view.dispatch(transaction);\n },\n },\n }),\n ];\n },\n});\n\nexport default Link;\n","import { Extension } from \"@tiptap/core\";\n\nexport interface UiState {\n aiGenerationIsSelection: boolean;\n aiGenerationIsLoading: boolean;\n aiGenerationActive: boolean;\n aiGenerationHasMessage: boolean;\n commentInputVisible: boolean;\n lockDragHandle: boolean;\n isDragging: boolean;\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n uiState: {\n aiGenerationSetIsSelection: (value: boolean) => ReturnType;\n aiGenerationSetIsLoading: (value: boolean) => ReturnType;\n aiGenerationShow: () => ReturnType;\n aiGenerationHide: () => ReturnType;\n aiGenerationHasMessage: (value: boolean) => ReturnType;\n\n commentInputShow: () => ReturnType;\n commentInputHide: () => ReturnType;\n\n setLockDragHandle: (value: boolean) => ReturnType;\n\n resetUiState: () => ReturnType;\n setIsDragging: (value: boolean) => ReturnType;\n };\n }\n\n interface Storage {\n uiState: UiState;\n }\n}\n\nexport const defaultUiState: UiState = {\n aiGenerationIsSelection: false,\n aiGenerationIsLoading: false,\n aiGenerationActive: false,\n aiGenerationHasMessage: false,\n commentInputVisible: false,\n lockDragHandle: false,\n isDragging: false,\n} as const;\n\nexport const UiState = Extension.create<UiState>({\n name: \"uiState\",\n\n addStorage() {\n return {\n uiState: { ...defaultUiState },\n };\n },\n\n addCommands() {\n const createBooleanSetter = (key: keyof UiState) => (value: boolean) => () => {\n this.storage[key] = value;\n return true;\n };\n\n const createToggle = (key: keyof UiState, value: boolean) => () => () => {\n this.storage[key] = value;\n return true;\n };\n\n return {\n // AI Generation commands\n aiGenerationSetIsSelection: createBooleanSetter(\"aiGenerationIsSelection\"),\n aiGenerationSetIsLoading: createBooleanSetter(\"aiGenerationIsLoading\"),\n aiGenerationHasMessage: createBooleanSetter(\"aiGenerationHasMessage\"),\n aiGenerationShow: createToggle(\"aiGenerationActive\", true),\n aiGenerationHide: createToggle(\"aiGenerationActive\", false),\n\n // Comment input commands\n commentInputShow: createToggle(\"commentInputVisible\", true),\n commentInputHide: createToggle(\"commentInputVisible\", false),\n\n // Drag handle commands\n setLockDragHandle: createBooleanSetter(\"lockDragHandle\"),\n setIsDragging: createBooleanSetter(\"isDragging\"),\n\n // Reset command\n resetUiState: () => () => {\n Object.assign(this.storage, { ...defaultUiState });\n return true;\n },\n };\n },\n\n onCreate() {\n this.storage = { ...defaultUiState };\n },\n});\n","import { findParentNode, isTextSelection } from \"@tiptap/core\";\nimport { Editor } from \"@tiptap/react\";\nimport { CellSelection, Rect, TableMap } from \"@tiptap/pm/tables\";\nimport { Selection, Transaction } from \"@tiptap/pm/state\";\n\n// 定义自定义节点类型数组,这些节点在选中时需要特殊处理\nconst customNodes = [\"image\", \"codeBlock\", \"link\", \"table\", \"title\"];\n\nexport const isTableGripSelected = (node: HTMLElement) => {\n let container = node;\n\n while (container && ![\"TD\", \"TH\"].includes(container.tagName)) {\n container = container.parentElement!;\n }\n\n const gripColumn =\n container && container.querySelector && container.querySelector(\"a.grip-column.selected\");\n const gripRow =\n container && container.querySelector && container.querySelector(\"a.grip-row.selected\");\n\n return !!(gripColumn || gripRow);\n};\n\n/**\n * 检查是否选中了自定义节点(如图片、代码块、链接、表格、标题等)\n * @param editor - Tiptap 编辑器实例\n * @param node - 当前的 HTML 元素节点\n * @returns 如果选中了自定义节点则返回 true\n */\nexport const isCustomNodeSelected = (editor: Editor, node: HTMLElement) => {\n // 从编辑器的选区状态中获取选区的起始位置(from)和结束位置(to)\n const { from, to } = editor.state.selection;\n // 初始化标题节点标志为 false\n let hasTitle = false;\n\n // 遍历选区范围内的所有节点\n editor.state.doc.nodesBetween(from, to, (node, pos) => {\n // 检查节点类型是否为 title(标题节点)\n if (node.type.name === \"title\") {\n // 如果是标题节点,将标志设为 true\n hasTitle = true;\n }\n });\n\n // 检查是否有任何自定义节点类型处于激活状态(使用 some 方法遍历 customNodes 数组)\n const hasCustomNode = customNodes.some((type) => editor.isActive(type));\n // 检查表格的拖拽手柄是否被选中\n const hasTableGrip = isTableGripSelected(node);\n // 如果有自定义节点被激活、或表格拖拽手柄被选中、或存在标题节点,则返回 true\n return hasCustomNode || hasTableGrip || hasTitle;\n};\n\n/**\n * 检查编辑器中是否选中了文本内容\n * @param editor - Tiptap 编辑器实例\n * @returns 如果选中了有效的文本内容则返回 true\n */\nexport const isTextSelected = ({ editor }: { editor: Editor }) => {\n // 从编辑器状态中解构出文档、选区对象,以及选区的 empty(是否为空)、from(起始位置)、to(结束位置)属性\n const {\n doc,\n selection,\n selection: { empty, from, to },\n } = editor.state;\n\n // 判断是否为空文本块:如果选区范围内没有文本内容且当前选区是文本选区类型\n const isEmptyTextBlock = !doc.textBetween(from, to).length && isTextSelection(selection);\n\n // 返回 true 的条件:选区不为空 && 不是空文本块 && 编辑器可编辑\n // 使用取反操作符,如果任何一个条件为 true 则返回 false\n return !(empty || isEmptyTextBlock || !editor.isEditable);\n};\n\n/**\n * 在当前选区中查找表格节点\n * @param selection - ProseMirror 选区对象\n * @returns 找到的表格节点,如果没有则返回 undefined\n */\nexport const findTable = (selection: Selection) =>\n // 使用 findParentNode 工具函数查找父节点\n findParentNode(\n // 判断条件:节点的 spec.tableRole 属性存在且值为 \"table\"\n (node) => node.type.spec.tableRole && node.type.spec.tableRole === \"table\"\n )(selection); // 传入选区对象执行查找\n\n/**\n * 检查指定的矩形区域是否被完全选中\n * @param rect - 矩形区域对象,包含 left、right、top、bottom 属性\n * @returns 返回一个函数,该函数接收选区对象并判断矩形区域是否被选中\n */\nexport const isRectSelected = (rect: Rect) => (selection: Selection) => {\n // 检查当前选区是否为单元格选区类型\n if (isCellSelection(selection)) {\n // 获取锚点单元格所在表格的映射对象(-1 表示向上查找一级父节点,即表格节点)\n const map = TableMap.get(selection.$anchorCell.node(-1));\n // 获取锚点单元格在表格中的起始位置\n const start = selection.$anchorCell.start(-1);\n // 获取指定矩形区域内的所有单元格索引\n const cells = map.cellsInRect(rect);\n // 获取当前实际选中的单元格索引(通过计算锚点和头部单元格之间的矩形区域)\n const selectedCells = map.cellsInRect(\n map.rectBetween(\n // 锚点单元格的相对位置(相对于表格起始位置)\n selection.$anchorCell.pos - start,\n // 头部单元格的相对位置(相对于表格起始位置)\n selection.$headCell.pos - start\n )\n );\n\n // 遍历矩形区域内的所有单元格\n for (let i = 0, count = cells.length; i < count; i += 1) {\n // 如果某个单元格不在实际选中的单元格列表中\n if (selectedCells.indexOf(cells[i]) === -1) {\n // 返回 false,表示矩形区域未被完全选中\n return false;\n }\n }\n\n // 所有单元格都被选中,返回 true\n return true;\n }\n // 如果不是单元格选区,返回 false\n return false;\n};\n\n/**\n * 类型守卫函数:检查选区是否为单元格选区类型\n * @param selection - ProseMirror 选区对象\n * @returns 如果是单元格选区则返回 true,并将类型缩窄为 CellSelection\n */\nexport const isCellSelection = (selection: Selection): selection is CellSelection =>\n selection instanceof CellSelection; // 使用 instanceof 检查选区是否为 CellSelection 实例\n\n/**\n * 检查指定列是否被完全选中\n * @param columnIndex - 列索引(从 0 开始)\n * @returns 返回一个函数,该函数接收选区对象并判断列是否被选中\n */\nexport const isColumnSelected = (columnIndex: number) => (selection: Selection) => {\n // 检查当前选区是否为单元格选区类型\n if (isCellSelection(selection)) {\n // 获取锚点单元格所在表格的映射对象\n const map = TableMap.get(selection.$anchorCell.node(-1));\n\n // 调用 isRectSelected 检查整列是否被选中\n return isRectSelected({\n left: columnIndex, // 矩形左边界为目标列索引\n right: columnIndex + 1, // 矩形右边界为目标列索引 + 1(表示单列)\n top: 0, // 矩形顶部从第一行开始\n bottom: map.height, // 矩形底部到最后一行(覆盖整列)\n })(selection);\n }\n\n // 如果不是单元格选区,返回 false\n return false;\n};\n\n/**\n * 检查指定行是否被完全选中\n * @param rowIndex - 行索引(从 0 开始)\n * @returns 返回一个函数,该函数接收选区对象并判断行是否被选中\n */\nexport const isRowSelected = (rowIndex: number) => (selection: Selection) => {\n // 检查当前选区是否为单元格选区类型\n if (isCellSelection(selection)) {\n // 获取锚点单元格所在表格的映射对象\n const map = TableMap.get(selection.$anchorCell.node(-1));\n\n // 调用 isRectSelected 检查整行是否被选中\n return isRectSelected({\n left: 0, // 矩形左边界从第一列开始\n right: map.width, // 矩形右边界到最后一列(覆盖整行)\n top: rowIndex, // 矩形顶部为目标行索引\n bottom: rowIndex + 1, // 矩形底部为目标行索引 + 1(表示单行)\n })(selection);\n }\n\n // 如果不是单元格选区,返回 false\n return false;\n};\n\n/**\n * 检查整个表格是否被完全选中\n * @param selection - ProseMirror 选区对象\n * @returns 如果整个表格被选中则返回 true\n */\nexport const isTableSelected = (selection: Selection) => {\n // 检查当前选区是否为单元格选区类型\n if (isCellSelection(selection)) {\n // 获取锚点单元格所在表格的映射对象\n const map = TableMap.get(selection.$anchorCell.node(-1));\n\n // 调用 isRectSelected 检查整个表格是否被选中\n return isRectSelected({\n left: 0, // 矩形左边界从第一列开始\n right: map.width, // 矩形右边界到最后一列\n top: 0, // 矩形顶部从第一行开始\n bottom: map.height, // 矩形底部到最后一行(覆盖整个表格)\n })(selection);\n }\n\n // 如果不是单元格选区,返回 false\n return false;\n};\n\n/**\n * 获取指定列中的所有单元格信息\n * @param columnIndex - 列索引(可以是单个数字或数字数组)\n * @returns 返回一个函数,该函数接收选区对象并返回单元格信息数组\n */\nexport const getCellsInColumn = (columnIndex: number | number[]) => (selection: Selection) => {\n // 在当前选区中查找表格节点\n const table = findTable(selection);\n if (table) {\n // 获取表格的映射对象\n const map = TableMap.get(table.node);\n // 将列索引统一转换为数组格式(如果传入的是单个数字,则转为包含该数字的数组)\n const indexes = Array.isArray(columnIndex) ? columnIndex : Array.from([columnIndex]);\n\n // 使用 reduce 遍历所有列索引,收集单元格信息\n return indexes.reduce(\n (acc, index) => {\n // 检查列索引是否有效(在 0 到表格宽度-1 之间)\n if (index >= 0 && index <= map.width - 1) {\n // 获取该列中所有单元格的位置索引\n const cells = map.cellsInRect({\n left: index, // 列的左边界\n right: index + 1, // 列的右边界(单列)\n top: 0, // 从第一行开始\n bottom: map.height, // 到最后一行(整列)\n });\n\n // 将新的单元格信息合并到累加器中\n return acc.concat(\n // 将每个单元格位置转换为包含节点信息的对象\n cells.map((nodePos) => {\n // 获取该位置的节点对象\n const node = table.node.nodeAt(nodePos) as any;\n // 计算节点在文档中的绝对位置(节点相对位置 + 表格起始位置)\n const pos = nodePos + table.start;\n\n // 返回单元格信息对象:pos(位置)、start(内容起始位置)、node(节点对象)\n return { pos, start: pos + 1, node };\n })\n );\n }\n\n // 如果索引无效,返回原累加器\n return acc;\n },\n // 初始值为空数组,类型为包含 pos、start、node 属性的对象数组\n [] as { pos: number; start: number; node: Node | null | undefined }[]\n );\n }\n // 如果没有找到表格,返回 null\n return null;\n};\n\n/**\n * 获取指定行中的所有单元格信息\n * @param rowIndex - 行索引(可以是单个数字或数字数组)\n * @returns 返回一个函数,该函数接收选区对象并返回单元格信息数组\n */\nexport const getCellsInRow = (rowIndex: number | number[]) => (selection: Selection) => {\n // 在当前选区中查找表格节点\n const table = findTable(selection);\n\n if (table) {\n // 获取表格的映射对象\n const map = TableMap.get(table.node);\n // 将行索引统一转换为数组格式(如果传入的是单个数字,则转为包含该数字的数组)\n const indexes = Array.isArray(rowIndex) ? rowIndex : Array.from([rowIndex]);\n\n // 使用 reduce 遍历所有行索引,收集单元格信息\n return indexes.reduce(\n (acc, index) => {\n // 检查行索引是否有效(在 0 到表格高度-1 之间)\n if (index >= 0 && index <= map.height - 1) {\n // 获取该行中所有单元格的位置索引\n const cells = map.cellsInRect({\n left: 0, // 从第一列开始\n right: map.width, // 到最后一列(整行)\n top: index, // 行的顶部边界\n bottom: index + 1, // 行的底部边界(单行)\n });\n\n // 将新的单元格信息合并到累加器中\n return acc.concat(\n // 将每个单元格位置转换为包含节点信息的对象\n cells.map((nodePos) => {\n // 获取该位置的节点对象\n const node = table.node.nodeAt(nodePos) as any;\n // 计算节点在文档中的绝对位置(节点相对位置 + 表格起始位置)\n const pos = nodePos + table.start;\n // 返回单元格信息对象:pos(位置)、start(内容起始位置)、node(节点对象)\n return { pos, start: pos + 1, node };\n })\n );\n }\n\n // 如果索引无效,返回原累加器\n return acc;\n },\n // 初始值为空数组,类型为包含 pos、start、node 属性的对象数组\n [] as { pos: number; start: number; node: Node | null | undefined }[]\n );\n }\n\n // 如果没有找到表格,返回 null\n return null;\n};\n\nconst select = (type: \"row\" | \"column\") => (index: number) => (tr: Transaction) => {\n const table = findTable(tr.selection);\n const isRowSelection = type === \"row\";\n\n if (table) {\n const map = TableMap.get(table.node);\n\n // Check if the index is valid\n if (index >= 0 && index < (isRowSelection ? map.height : map.width)) {\n const left = isRowSelection ? 0 : index;\n const top = isRowSelection ? index : 0;\n const right = isRowSelection ? map.width : index + 1;\n const bottom = isRowSelection ? index + 1 : map.height;\n\n const cellsInFirstRow = map.cellsInRect({\n left,\n top,\n right: isRowSelection ? right : left + 1,\n bottom: isRowSelection ? top + 1 : bottom,\n });\n\n const cellsInLastRow =\n bottom - top === 1\n ? cellsInFirstRow\n : map.cellsInRect({\n left: isRowSelection ? left : right - 1,\n top: isRowSelection ? bottom - 1 : top,\n right,\n bottom,\n });\n\n const head = table.start + cellsInFirstRow[0];\n const anchor = table.start + cellsInLastRow[cellsInLastRow.length - 1];\n const $head = tr.doc.resolve(head);\n const $anchor = tr.doc.resolve(anchor);\n\n return tr.setSelection(new CellSelection($anchor, $head));\n }\n }\n return tr;\n};\n\nexport const selectColumn = select(\"column\");\n\nexport const selectRow = select(\"row\");\n","import { mergeAttributes, Node } from \"@tiptap/core\";\nimport { Plugin } from \"@tiptap/pm/state\";\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\nimport { getCellsInColumn, isRowSelected, selectRow } from \"./utils\";\n\nexport interface TableCellOptions {\n HTMLAttributes: Record<string, any>;\n}\n\nexport const TableCell = Node.create<TableCellOptions>({\n name: \"tableCell\",\n content: \"block+\",\n tableRole: \"cell\",\n isolating: true,\n addOptions() {\n return {\n HTMLAttributes: {},\n };\n },\n parseHTML() {\n return [{ tag: \"td\" }];\n },\n renderHTML({ HTMLAttributes }) {\n return [\"td\", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];\n },\n addAttributes() {\n return {\n colspan: {\n default: 1,\n parseHTML: (element) => {\n const colspan = element.getAttribute(\"colspan\");\n const value = colspan ? parseInt(colspan, 10) : 1;\n\n return value;\n },\n },\n rowspan: {\n default: 1,\n parseHTML: (element) => {\n const rowspan = element.getAttribute(\"rowspan\");\n const value = rowspan ? parseInt(rowspan, 10) : 1;\n\n return value;\n },\n },\n colwidth: {\n default: null,\n parseHTML: (element) => {\n const colwidth = element.getAttribute(\"colwidth\");\n const value = colwidth ? colwidth.split(\",\").map((width) => parseInt(width, 10)) : null;\n\n return value;\n },\n },\n style: {\n default: null,\n },\n };\n },\n addProseMirrorPlugins() {\n const { isEditable } = this.editor;\n\n return [\n new Plugin({\n props: {\n decorations: (state) => {\n if (!isEditable) {\n return DecorationSet.empty;\n }\n\n const { doc, selection } = state;\n const decorations: Decoration[] = [];\n const cells = getCellsInColumn(0)(selection);\n\n if (cells) {\n cells.forEach(({ pos }: { pos: number }, index: number) => {\n decorations.push(\n Decoration.widget(pos + 1, () => {\n const rowSelected = isRowSelected(index)(selection);\n let className = \"grip-row\";\n\n if (rowSelected) {\n className += \" selected\";\n }\n\n if (index === 0) {\n className += \" first\";\n }\n\n if (index === cells.length - 1) {\n className += \" last\";\n }\n\n const grip = document.createElement(\"a\");\n\n grip.className = className;\n grip.addEventListener(\"mousedown\", (event) => {\n event.preventDefault();\n event.stopImmediatePropagation();\n\n this.editor.view.dispatch(selectRow(index)(this.editor.state.tr));\n });\n\n return grip;\n })\n );\n });\n }\n\n return DecorationSet.create(doc, decorations);\n },\n },\n }),\n ];\n },\n});\n","import { TableHeader as TiptapTableHeader } from \"@tiptap/extension-table\";\nimport { Plugin } from \"@tiptap/pm/state\";\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\n\nimport { getCellsInRow, isColumnSelected, selectColumn } from \"./utils\";\n\nexport { type TableHeaderOptions } from \"@tiptap/extension-table\";\n\nexport const TableHeader = TiptapTableHeader.extend({\n addProseMirrorPlugins() {\n const { isEditable } = this.editor;\n\n return [\n new Plugin({\n props: {\n decorations: (state) => {\n if (!isEditable) {\n return DecorationSet.empty;\n }\n\n const { doc, selection } = state;\n const decorations: Decoration[] = [];\n const cells = getCellsInRow(0)(selection);\n\n if (cells) {\n cells.forEach(({ pos }: { pos: number }, index: number) => {\n decorations.push(\n Decoration.widget(pos + 1, () => {\n const colSelected = isColumnSelected(index)(selection);\n let className = \"grip-column\";\n\n if (colSelected) {\n className += \" selected\";\n }\n\n if (index === 0) {\n className += \" first\";\n }\n\n if (index === cells.length - 1) {\n className += \" last\";\n }\n\n const grip = document.createElement(\"a\");\n\n grip.className = className;\n grip.addEventListener(\"mousedown\", (event) => {\n event.preventDefault();\n event.stopImmediatePropagation();\n\n this.editor.view.dispatch(selectColumn(index)(this.editor.state.tr));\n });\n\n return grip;\n })\n );\n });\n }\n\n return DecorationSet.create(doc, decorations);\n },\n },\n }),\n ];\n },\n});\n\nexport default TableHeader;\n","import { TableRow as TiptapTableRow } from \"@tiptap/extension-table\";\nexport { type TableRowOptions } from \"@tiptap/extension-table\";\n\nexport const TableRow = TiptapTableRow.extend({\n allowGapCursor: false,\n // content: 'tableCell*',\n});\n\nexport default TableRow;\n","import { Table as TipTable } from \"@tiptap/extension-table\";\n// import { TableCell, type TableCellOptions } from '@tiptap/extension-table-cell'\nimport { TableCell, type TableCellOptions } from \"./cell\";\n// import { TableHeader, type TableHeaderOptions } from '@tiptap/extension-table-header'\nimport { TableHeader, type TableHeaderOptions } from \"./header\";\n// import { TableRow, type TableRowOptions } from '@tiptap/extension-table-row'\nimport { TableRow, type TableRowOptions } from \"./row\";\n// import { type TableCellBackgroundOptions, TableCellBackground } from './widget/cell-background'\n\nexport interface TableOptions {\n HTMLAttributes: Record<string, any>;\n resizable: boolean;\n handleWidth?: number;\n cellMinWidth?: number;\n lastColumnResizable: boolean;\n allowTableNodeSelection: boolean;\n tableRow?: Partial<TableRowOptions>;\n tableHeader?: Partial<TableHeaderOptions>;\n tableCell?: Partial<TableCellOptions>;\n // tableCellBackground: Partial<TableCellBackgroundOptions>\n}\n\nexport const Table = TipTable.extend<TableOptions>({\n addOptions() {\n return {\n ...this.parent?.(),\n resizable: true,\n lastColumnResizable: true,\n allowTableNodeSelection: true,\n HTMLAttributes: {},\n };\n },\n\n addExtensions() {\n return [\n TableRow.configure(this.options.tableRow),\n TableHeader.configure(this.options.tableHeader),\n TableCell.configure(this.options.tableCell),\n ];\n },\n});\n\nexport default Table;\n","import StarterKit from \"@tiptap/starter-kit\";\nimport { TextStyleKit, Color, BackgroundColor } from \"@tiptap/extension-text-style\";\nimport TextAlign from \"@tiptap/extension-text-align\";\nimport { Placeholder, Dropcursor, Gapcursor, Selection, Focus } from \"@tiptap/extensions\";\nimport { TaskItem, TaskList } from \"@tiptap/extension-list\";\n// import { TableKit } from \"@tiptap/extension-table\"\nimport FileHandler from \"@tiptap/extension-file-handler\";\nimport CodeBlockLowlight from \"@tiptap/extension-code-block-lowlight\";\nimport { common, createLowlight } from \"lowlight\";\nimport { type Editor, type Extensions, ReactNodeViewRenderer } from \"@tiptap/react\";\nimport { NodeViewCodeBlock } from \"./extension-code-block/code-block\";\nimport Image from \"./extension-image\";\n\nimport css from \"highlight.js/lib/languages/css\";\nimport js from \"highlight.js/lib/languages/javascript\";\nimport ts from \"highlight.js/lib/languages/typescript\";\nimport html from \"highlight.js/lib/languages/xml\";\nimport \"highlight.js/styles/github.css\";\nimport Link from \"./extension-link\";\nimport { UiState } from \"./ui-state-extension\";\nimport Table from \"./extension-table\";\nimport { IMAGE_MAX_SIZE } from \"../utils/constant\";\nimport { Mathematics } from \"@tiptap/extension-mathematics\";\n\nconst lowlight = createLowlight(common);\n\n// you can also register individual languages\nlowlight.register(\"html\", html);\nlowlight.register(\"css\", css);\nlowlight.register(\"js\", js);\nlowlight.register(\"ts\", ts);\n\nexport interface GetExtensionsProps {\n // 上传文件\n uploadFn?: (file: File, editor: Editor) => Promise<string>;\n}\n\nconst getExtensions = (props: GetExtensionsProps) => {\n const { uploadFn } = props;\n\n const extensions: Extensions = [\n TextStyleKit,\n StarterKit,\n Placeholder.configure({\n placeholder: ({ node }) => {\n const nodeTypeName = node?.type?.name;\n if (nodeTypeName === \"title\") {\n return \"未命名文档\";\n }\n if (nodeTypeName === \"heading\") {\n return `标题${node.attrs.level}`;\n }\n if (nodeTypeName === \"codeBlock\") {\n return \"请输入代码\";\n }\n if (nodeTypeName === \"table\") {\n return \"\";\n }\n return `请输入内容`;\n },\n }),\n Focus.configure({\n className: \"node-focused\",\n mode: \"all\",\n }),\n TextAlign.configure({\n types: [\"heading\", \"paragraph\"],\n }),\n Color,\n BackgroundColor,\n TaskList,\n TaskItem,\n Table,\n Gapcursor,\n Image.configure({\n allowedMimeTypes: [\"image/*\"],\n maxFileSize: IMAGE_MAX_SIZE * 1024 * 1024,\n allowBase64: false,\n uploadFn: uploadFn,\n onActionSuccess(props) {\n console.log(\"Image action success:\", props);\n },\n onValidationError(errors) {\n errors.forEach((error) => {});\n },\n }),\n Dropcursor,\n FileHandler.configure({\n allowedMimeTypes: [\"image/png\", \"image/jpeg\", \"image/gif\", \"image/webp\"],\n onDrop: (currentEditor, files, pos) => {\n files.forEach(async (file) => {\n const localSrc = URL.createObjectURL(file);\n\n currentEditor\n .chain()\n .insertContentAt(pos, {\n type: \"image\",\n attrs: {\n src: localSrc,\n fileName: file.name,\n },\n })\n .focus()\n .run();\n\n if (uploadFn) {\n try {\n const remoteSrc = await uploadFn(file, currentEditor);\n currentEditor.commands.updateAttributes(\"image\", { src: remoteSrc });\n } catch (error) {\n console.error(\"上传失败:\", error);\n } finally {\n URL.revokeObjectURL(localSrc);\n }\n }\n });\n },\n onPaste: (currentEditor, files, htmlContent) => {\n if (htmlContent) return false;\n\n files.forEach(async (file) => {\n const localSrc = URL.createObjectURL(file);\n\n currentEditor\n .chain()\n .insertContentAt(currentEditor.state.selection.anchor, {\n type: \"image\",\n attrs: { src: localSrc },\n })\n .focus()\n .run();\n\n if (uploadFn) {\n try {\n const remoteSrc = await uploadFn(file, currentEditor);\n currentEditor.commands.updateAttributes(\"image\", { src: remoteSrc });\n } catch (error) {\n console.error(\"粘贴上传失败:\", error);\n } finally {\n URL.revokeObjectURL(localSrc);\n }\n }\n });\n },\n }),\n Selection.configure({\n className: \"selection\",\n }),\n Link.configure({\n openOnClick: \"whenNotEditable\",\n }),\n CodeBlockLowlight.extend({\n addNodeView() {\n return ReactNodeViewRenderer(NodeViewCodeBlock);\n },\n }).configure({\n lowlight: lowlight,\n }),\n UiState,\n Mathematics,\n ];\n\n return extensions;\n};\n\nexport { getExtensions };\n","\"use client\";\nimport React, { useCallback } from \"react\";\nimport { BubbleMenu } from \"@tiptap/react/menus\";\nimport { NodeSelection } from \"@tiptap/pm/state\";\nimport { useCurrentEditor } from \"../../context\";\nimport { Separator } from \"../../components/ui/separator\";\nimport TextSelector from \"../../toolbar/components/selectors/text-selector\";\nimport LinkSelector from \"../../toolbar/components/selectors/link-selector\";\nimport ColorSelector from \"../../toolbar/components/selectors/color-selector\";\nimport BgSelector from \"../../toolbar/components/selectors/bg-selector\";\nimport { textFormatActions } from \"../../toolbarAction\";\nimport { isCustomNodeSelected, isTextSelected } from \"../../extensions/extension-table/utils\";\n\nconst BubbleMenuText = () => {\n const { editor } = useCurrentEditor();\n\n const shouldShow = useCallback(\n (props: any) => {\n // 判断富文本编辑器中是否有选中文本,如果有选中文本,则显示气泡菜单\n const { view, from, to } = props;\n if (!view || view.dragging) {\n return false;\n }\n\n const domAtPos = view.domAtPos(from || 0).node as HTMLElement;\n const nodeDOM = view.nodeDOM(from || 0) as HTMLElement;\n const node = nodeDOM || domAtPos;\n\n const isNodeSelection = editor.state.selection instanceof NodeSelection;\n\n if (isCustomNodeSelected(editor, node) || isNodeSelection) {\n return false;\n }\n\n // 检查是否选中了文本\n return isTextSelected({ editor });\n },\n [editor]\n );\n\n return (\n <BubbleMenu\n editor={editor}\n pluginKey=\"textBubbleMenu\"\n shouldShow={shouldShow}\n updateDelay={100}\n options={{\n placement: \"top-start\",\n onShow: () => {},\n onHide: () => {\n console.log(\"textBubbleMenu onHidden\");\n },\n }}\n appendTo={() => document.body}\n >\n <div className=\"flex h-[52px] items-center justify-center gap-3 rounded-xl border-primary/10 bg-white px-4 shadow-[0px_0px_20px_0px_rgba(0,0,0,0.1)]\">\n <Separator orientation=\"vertical\" className=\"bg-line h-4\" />\n <TextSelector textFormatActions={textFormatActions} />\n <LinkSelector />\n <ColorSelector />\n <BgSelector />\n </div>\n </BubbleMenu>\n );\n};\n\nexport default BubbleMenuText;\n","import { useCurrentEditor } from \"../../context\";\nimport { useEditorDerivedState } from \"../../hooks/useEditorDerivedState\";\nimport { ToolbarButton } from \"../../toolbar/components/action-button\";\nimport { nodeFormatActions } from \"../../toolbarAction\";\n\nconst Menus = ({ currentNode, currentNodePos }: any) => {\n const { editor } = useCurrentEditor();\n\n const state = useEditorDerivedState();\n\n return (\n <div className={\"gap flex flex-wrap gap-[12px]\"}>\n {nodeFormatActions.map((item) => (\n <ToolbarButton\n key={item.id}\n isActive={item.isActive?.(state)}\n onClick={() => item.onClick(editor)}\n disabled={item.disabled?.(state)}\n tooltip={item.label}\n shortcutKeys={item.shortcutKeys}\n >\n <item.icon size={20} />\n </ToolbarButton>\n ))}\n </div>\n );\n};\n\nexport default Menus;\n","\"use client\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport type { Node as TiptapNode } from \"@tiptap/pm/model\";\nimport { offset } from \"@floating-ui/react\";\nimport { DragHandle } from \"@tiptap/extension-drag-handle-react\";\nimport copyTextToClipboard from \"copy-to-clipboard\";\nimport React from \"react\";\nimport { useCurrentEditor } from \"../../context\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"../../components/ui/dropdown-menu\";\nimport { Button } from \"../../components/ui/button\";\nimport { Copy, GripVertical, Trash2 } from \"lucide-react\";\nimport Menus from \"./menus\";\n\nconst BubbleMenuBlock: React.FC<any> = ({\n withSlashCommandTrigger = true,\n mobileBreakpoint = 768,\n ...props\n}) => {\n const { editor } = useCurrentEditor();\n const [open, setOpen] = useState(false);\n const [node, setNode] = useState<TiptapNode | null>(null);\n const [nodePos, setNodePos] = useState<number>(-1);\n\n const handleNodeChange = useCallback((data: any) => {\n if (data.node) setNode(data.node);\n setNodePos(data.pos);\n }, []);\n\n useEffect(() => {\n if (!editor) return;\n editor.commands.setLockDragHandle(open);\n editor.commands.setMeta(\"lockDragHandle\", open);\n }, [editor, open]);\n\n const mainAxisOffset = 16;\n\n const dynamicPositions = useMemo(() => {\n return {\n middleware: [\n offset((props) => {\n const { rects } = props;\n const nodeHeight = rects.reference.height;\n const dragHandleHeight = rects.floating.height;\n\n const crossAxis = nodeHeight / 2 - dragHandleHeight / 2;\n\n return {\n mainAxis: mainAxisOffset,\n // if height is more than 40px, then it's likely a block node\n crossAxis: nodeHeight > 40 ? 0 : crossAxis,\n };\n }),\n ],\n };\n }, []);\n\n const onElementDragStart = useCallback(() => {\n if (!editor) return;\n editor.commands.setIsDragging(true);\n }, [editor]);\n\n const onElementDragEnd = useCallback(() => {\n if (!editor) return;\n editor.commands.setIsDragging(false);\n\n setTimeout(() => {\n editor.view.dom.blur();\n editor.view.focus();\n }, 0);\n }, [editor]);\n\n const copyToClipboard = () => {\n editor.chain().setMeta(\"hideDragHandle\", true).run();\n editor.chain().setNodeSelection(nodePos).run();\n const text = node?.textContent;\n console.log(text);\n copyTextToClipboard(text || \"\");\n };\n\n const removeNode = () => {\n editor\n .chain()\n .setMeta(\"hideDragHandle\", true)\n .setNodeSelection(nodePos)\n .deleteSelection()\n .run();\n };\n\n if (!editor) return null;\n\n return (\n <div\n style={\n {\n \"--drag-handle-main-axis-offset\": `${mainAxisOffset}px`,\n } as React.CSSProperties\n }\n >\n <DragHandle\n editor={editor}\n onNodeChange={handleNodeChange}\n computePositionConfig={dynamicPositions}\n onElementDragStart={onElementDragStart}\n onElementDragEnd={onElementDragEnd}\n {...props}\n >\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"z-1 drag-handle flex h-8 w-8 items-center justify-center rounded-lg bg-gray-200 hover:bg-gray-300\"\n >\n {/* {selectedNodeIcon} */}\n <GripVertical size={16} />\n </Button>\n </DropdownMenuTrigger>\n\n <DropdownMenuContent\n className=\"z-50 w-[164px] border-none p-[8px] shadow-[0_0px_12px_0px_rgba(0,0,0,0.1)]\"\n align=\"start\"\n side=\"bottom\"\n sideOffset={0}\n >\n {[\"image\", \"table\"].indexOf(node?.type.name || \"\") === -1 && (\n <>\n <Menus currentNode={node} currentNodePos={nodePos} />\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"flex cursor-pointer gap-3\" onClick={copyToClipboard}>\n <Copy size={16} />\n <span>复制</span>\n </DropdownMenuItem>\n </>\n )}\n\n <DropdownMenuItem onClick={removeNode} className=\"flex cursor-pointer gap-3\">\n <Trash2 size={16} />\n <span>删除</span>\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </DragHandle>\n </div>\n );\n};\n\nexport default BubbleMenuBlock;\n","import { memo, useCallback } from \"react\";\nimport { BubbleMenu } from \"@tiptap/react/menus\";\nimport {\n ArrowLeftToLine,\n ArrowRightToLine,\n ArrowUpToLine,\n ArrowDownToLine,\n TableCellsMerge,\n TableCellsSplit,\n Trash2,\n} from \"lucide-react\";\nimport { useEditorState } from \"@tiptap/react\";\nimport { useCurrentEditor } from \"../../context\";\nimport {\n getCellsInColumn,\n getCellsInRow,\n isCellSelection,\n isColumnSelected,\n isRowSelected,\n isTableSelected,\n} from \"../../extensions/extension-table/utils\";\nimport { TABLE_MAX_COLUMN_SIZE, TABLE_MAX_ROW_SIZE } from \"../../utils/constant\";\nimport { ToolbarButton } from \"../../toolbar/components/action-button\";\n\nfunction BubbleMenuTable() {\n const { editor } = useCurrentEditor();\n\n const selectorState = useEditorState({\n editor,\n selector: (ctx) => {\n const { selection, doc } = ctx.editor.state;\n const colCells = getCellsInColumn(0)(selection);\n const rowCells = getCellsInRow(0)(selection);\n let rowCount = 0;\n let columnCount = 0;\n\n doc.nodesBetween(selection.from, selection.to, (node) => {\n // 只关注块级节点\n if (node.type.name === \"table\") {\n rowCount = node.childCount;\n columnCount = node.firstChild ? node.firstChild.childCount : 0;\n }\n });\n\n return {\n canAddColumnBefore: ctx.editor.can().addColumnBefore(),\n canAddColumnAfter: ctx.editor.can().addColumnAfter(),\n canDeleteColumn: ctx.editor.can().deleteColumn(),\n canAddRowBefore: ctx.editor.can().addRowBefore(),\n canAddRowAfter: ctx.editor.can().addRowAfter(),\n canDeleteRow: ctx.editor.can().deleteRow(),\n canSplitCell: ctx.editor.can().splitCell(),\n canMergeCell: ctx.editor.can().mergeCells(),\n canDeleteTable: ctx.editor.can().deleteTable(),\n isRowGripSelected: colCells?.some((_, index) => isRowSelected(index)(selection)),\n isColumnGripSelected: rowCells?.some((_, index) => isColumnSelected(index)(selection)),\n rowCount,\n columnCount,\n };\n },\n });\n\n const shouldShow = useCallback(({ editor, state, view, from }: any) => {\n if (!state || !from) {\n return false;\n }\n\n const domAtPos = view.domAtPos(from).node as HTMLElement;\n const nodeDOM = view.nodeDOM(from) as HTMLElement;\n const node = nodeDOM || domAtPos;\n if (!editor.isActive(\"table\") || !node || isTableSelected(state.selection)) {\n return false;\n }\n return isCellSelection(state.selection);\n }, []);\n\n const onAddColumn = (type: \"before\" | \"after\") => {\n console.log(\"onAddColumn selectorState.columnCount\", selectorState.columnCount);\n if (selectorState.columnCount >= TABLE_MAX_COLUMN_SIZE) {\n // toast.warning(\n // `表格列数已达上限(${TABLE_MAX_COLUMN_SIZE}),请插入新的表格以继续编辑`,\n // )\n return;\n }\n type === \"before\"\n ? editor.chain().focus().addColumnBefore().run()\n : editor.chain().focus().addColumnAfter().run();\n };\n\n const onAddRow = (type: \"before\" | \"after\") => {\n console.log(\"onAddRow selectorState.rowCount\", selectorState.rowCount);\n if (selectorState.rowCount >= TABLE_MAX_ROW_SIZE) {\n // toast.error(\n // `表格行数已达上限(${TABLE_MAX_ROW_SIZE}),请插入新的表格继续编辑`,\n // )\n return;\n }\n type === \"before\"\n ? editor.chain().focus().addRowBefore().run()\n : editor.chain().focus().addRowAfter().run();\n };\n\n return (\n <BubbleMenu\n editor={editor}\n pluginKey=\"tableBubbleMenu\"\n shouldShow={shouldShow}\n updateDelay={0}\n className=\"z-20\"\n options={{\n offset: {\n mainAxis: 0,\n crossAxis: 8,\n },\n placement: \"top-start\",\n onShow: () => {},\n onHide: () => {\n console.log(\"tableBubbleMenu onHidden\");\n },\n }}\n >\n <div className=\"border-line z-50 flex w-full items-center gap-3 rounded-xl border bg-white px-3 py-2 shadow-[0px_0px_20px_0px_rgba(0,0,0,0.1)]\">\n {selectorState.isColumnGripSelected && (\n <ToolbarButton\n tooltip=\"在前面插入列\"\n onClick={() => {\n onAddColumn(\"before\");\n }}\n tooltip-options={{\n sideOffset: 15,\n }}\n disabled={!selectorState.canAddColumnBefore}\n >\n <ArrowLeftToLine size={20} />\n </ToolbarButton>\n )}\n {selectorState.isColumnGripSelected && (\n <ToolbarButton\n tooltip=\"在后面插入列\"\n onClick={() => {\n onAddColumn(\"after\");\n }}\n tooltip-options={{\n sideOffset: 15,\n }}\n disabled={!selectorState.canAddColumnAfter}\n >\n <ArrowRightToLine size={20} />\n </ToolbarButton>\n )}\n {selectorState.isColumnGripSelected && (\n <ToolbarButton\n tooltip=\"删除列\"\n onClick={() => {\n editor.chain().focus().deleteColumn().run();\n }}\n tooltip-options={{\n sideOffset: 15,\n }}\n disabled={!selectorState.canDeleteColumn}\n >\n <Trash2 size={20} />\n </ToolbarButton>\n )}\n {selectorState.isRowGripSelected && (\n <ToolbarButton\n onClick={() => {\n onAddRow(\"before\");\n }}\n tooltip=\"在上面插入行\"\n tooltip-options={{\n sideOffset: 15,\n }}\n disabled={!selectorState.canAddRowBefore}\n >\n <ArrowUpToLine size={20} />\n </ToolbarButton>\n )}\n {selectorState.isRowGripSelected && (\n <ToolbarButton\n onClick={() => {\n onAddRow(\"after\");\n }}\n tooltip=\"在下面插入行\"\n tooltip-options={{\n sideOffset: 15,\n }}\n disabled={!selectorState.canAddRowBefore}\n >\n <ArrowDownToLine size={20} />\n </ToolbarButton>\n )}\n {selectorState.isRowGripSelected && (\n <ToolbarButton\n onClick={() => {\n editor.chain().focus().deleteRow().run();\n }}\n tooltip=\"删除行\"\n tooltip-options={{\n sideOffset: 15,\n }}\n disabled={!selectorState.canDeleteRow}\n >\n <Trash2 size={20} />\n </ToolbarButton>\n )}\n <ToolbarButton\n onClick={() => {\n editor.chain().focus().mergeCells().run();\n }}\n tooltip=\"合并单元格\"\n tooltip-options={{\n sideOffset: 15,\n }}\n disabled={!selectorState.canMergeCell}\n >\n <TableCellsMerge size={20} />\n </ToolbarButton>\n\n <ToolbarButton\n onClick={() => {\n editor.chain().focus().splitCell().run();\n }}\n tooltip=\"拆分单元格\"\n tooltip-options={{\n sideOffset: 15,\n }}\n disabled={!selectorState.canSplitCell}\n >\n <TableCellsSplit size={20} />\n </ToolbarButton>\n </div>\n </BubbleMenu>\n );\n}\n\nexport default memo(BubbleMenuTable);\n","\"use client\";\nimport { useEditor, EditorContent } from \"@tiptap/react\";\nimport { Toolbar, type ToolbarProps } from \"../toolbar/index\";\nimport { EditorProvider } from \"../context\";\nimport { getExtensions } from \"../extensions\";\nimport BubbleMenuText from \"../bubble-menu/bubble-menu-text\";\nimport BubbleMenuBlock from \"../bubble-menu/bubble-menu-block\";\nimport BubbleMenuTable from \"../bubble-menu/bubble-menu-table\";\nimport { cn } from \"../lib/utils\";\nimport { type Editor} from \"@tiptap/core\";\n\nexport interface TiptapComponentsProps {\n value: string;\n onChange: (value: string) => void;\n onImageUpload?: () => Promise<string>;\n className?: string;\n showToolbar?: boolean;\n showBubbleMenu?: boolean;\n showTableMenu?: boolean;\n showBlockMenu?: boolean;\n toolbarProps?: ToolbarProps;\n onInitEditor?: (editor: Editor) => void;\n}\n\nexport const TiptapComponent = ({\n value,\n onChange,\n onImageUpload,\n className,\n showToolbar = true,\n showBubbleMenu = true,\n showTableMenu = true,\n showBlockMenu = true,\n toolbarProps,\n onInitEditor\n}: TiptapComponentsProps) => {\n const editor = useEditor({\n immediatelyRender: false,\n extensions: getExtensions({ uploadFn: onImageUpload }),\n content: value,\n editorProps: {\n attributes: {\n class: \"w-full focus:outline-none\",\n },\n },\n onUpdate: ({ editor }) => {\n onChange(editor.getHTML());\n },\n onCreate: ({editor}) => {\n onInitEditor?.(editor);\n },\n });\n\n if (!editor) {\n return null;\n }\n\n return (\n <EditorProvider editor={editor}>\n {showToolbar && <Toolbar {...toolbarProps} />}\n {showBubbleMenu && <BubbleMenuText />}\n {showTableMenu && <BubbleMenuTable />}\n {showBlockMenu && <BubbleMenuBlock />}\n <EditorContent editor={editor} className={cn(\"w-full\", className)} />\n </EditorProvider>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["t","useTheme","Sonner","CircleCheck","Info","TriangleAlert","OctagonX","LoaderCircle","x","flip","placements","sides","side","b","placement","overflow","offset","x","shift","min","max","editor","Toggle: React.ForwardRefExoticComponent<\r\n ToggleProps & React.RefAttributes<React.ElementRef<typeof TogglePrimitive.Root>>\r\n>","React","TogglePrimitive","TooltipPrimitive","React","Slot","size","React","Heading1","Heading2","Heading3","Heading4","Heading5","Heading6","undo: IActionItem","Undo2","redo: IActionItem","Redo2","paragraph: IActionItem","Type","blockquote: IActionItem","Quote","codeBlock: IActionItem","CodeXml","orderedList: IActionItem","ListOrdered","bulletList: IActionItem","List","todoList: IActionItem","ListTodo","leftAlign: IActionItem","AlignLeft","rightAlign: IActionItem","AlignRight","centerAlign: IActionItem","AlignCenter","justifyAlign: IActionItem","AlignJustify","textAlignActions: IActionItem[]","bold: IActionItem","BoldIcon","italic: IActionItem","ItalicIcon","underline: IActionItem","UnderlineIcon","strike: IActionItem","StrikethroughIcon","inlineCode: IActionItem","Code","textFormatActions: IActionItem[]","nodeFormatActions: IActionItem[]","DropdownMenuPrimitive","autoUpdate","DragHandle","GripVertical","Menus","Copy","Trash2","node","TableMap","CellSelection","editor","BubbleMenu","ArrowLeftToLine","ArrowRightToLine","Trash2","ArrowUpToLine","ArrowDownToLine","TableCellsMerge","TableCellsSplit","SeparatorPrimitive","textFormatActions","PopoverPrimitive","LabelPrimitive","React","LinkIcon","TEXT_COLORS: string[]","editor","Popover","Baseline","ChevronDown","HIGHLIGHT_COLORS: string[]","editor","Popover","Highlighter","ChevronDown","BubbleMenu","NodeSelection","TextSelector","LinkSelector","ColorSelector","BgSelector","Check","Copy","ChevronDown","ChevronRight","NodeViewContent","NodeViewWrapper","size","validFiles: T[]","errors: FileError[]","ImageActions: React.FC<any>","React","AlignLeft","AlignCenter","AlignRight","Maximize","Trash2","React","LoaderCircle","React","directionStyles","ImageViewBlock: React.FC<NodeViewProps>","React","NodeViewWrapper","LoaderCircle","ControlledZoom","Trash2","TiptapImage","TiptapLink","Plugin","TextSelection","Node","Plugin","DecorationSet","decorations: Decoration[]","Decoration","TiptapTableHeader","Plugin","DecorationSet","decorations: Decoration[]","Decoration","TiptapTableRow","Table","TipTable","lowlight","common","html","css","js","ts","TextStyleKit","StarterKit","Placeholder","Focus","TextAlign","Color","BackgroundColor","TaskList","TaskItem","Table","Gapcursor","Image","Dropcursor","FileHandler","Selection","Link","CodeBlockLowlight","Mathematics","nodeFormatActions","ChevronDown","Check","ChevronDown","Check","ImagePlus","React","Table","DEFAULT_CONFIG: ToolbarItemKey[]","NodeSelector","TextSelector","LinkSelector","ColorSelector","BgSelector","AlignSelector","InsetImageSelector","TableActionSelector","React","editor","BubbleMenuText","BubbleMenuTable","BubbleMenuBlock","EditorContent"],"sources":["../../../node_modules/next-themes/dist/index.mjs","../src/components/ui/sonner.tsx","../../../node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs","../../../node_modules/@floating-ui/core/dist/floating-ui.core.mjs","../../../node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs","../src/context/index.tsx","../src/hooks/useEditorDerivedState.ts","../src/lib/utils.ts","../src/components/ui/toggle.tsx","../src/components/ui/tooltip.tsx","../src/components/ui/button.tsx","../src/toolbar/components/action-button/index.tsx","../src/toolbarAction.tsx","../src/bubble-menu/bubble-menu-block/menus.tsx","../src/components/ui/dropdown-menu.tsx","../src/bubble-menu/bubble-menu-block/index.tsx","../src/extensions/extension-table/utils.tsx","../src/utils/constant.ts","../src/bubble-menu/bubble-menu-table/index.tsx","../src/components/ui/separator.tsx","../src/toolbar/components/selectors/text-selector.tsx","../src/components/ui/popover.tsx","../src/components/ui/label.tsx","../src/components/ui/input.tsx","../src/extensions/extension-link/widget/edit-panel.tsx","../src/toolbar/components/selectors/link-selector.tsx","../src/toolbar/components/selectors/color-selector.tsx","../src/toolbar/components/selectors/bg-selector.tsx","../src/bubble-menu/bubble-menu-text/index.tsx","../src/extensions/extension-code-block/code-block.tsx","../src/utils/index.ts","../src/extensions/extension-image/hooks/use-drag-resize.ts","../src/extensions/extension-image/components/image-actions.tsx","../src/extensions/extension-image/components/image-overlay.tsx","../src/extensions/extension-image/components/resize-handle.tsx","../src/extensions/extension-image/components/image-view-block.tsx","../src/extensions/extension-image/index.ts","../src/extensions/extension-link/index.ts","../src/extensions/extension-table/cell.tsx","../src/extensions/extension-table/header.tsx","../src/extensions/extension-table/row.tsx","../src/extensions/extension-table/index.tsx","../src/extensions/index.ts","../src/toolbar/components/selectors/node-selector.tsx","../src/toolbar/components/selectors/align-selector.tsx","../src/toolbar/components/selectors/inset-image-selector.tsx","../src/toolbar/components/selectors/table-action-selector.tsx","../src/toolbar/index.tsx","../src/editor/index.tsx"],"sourcesContent":["\"use client\";import*as t from\"react\";var M=(e,i,s,u,m,a,l,h)=>{let d=document.documentElement,w=[\"light\",\"dark\"];function p(n){(Array.isArray(e)?e:[e]).forEach(y=>{let k=y===\"class\",S=k&&a?m.map(f=>a[f]||f):m;k?(d.classList.remove(...S),d.classList.add(a&&a[n]?a[n]:n)):d.setAttribute(y,n)}),R(n)}function R(n){h&&w.includes(n)&&(d.style.colorScheme=n)}function c(){return window.matchMedia(\"(prefers-color-scheme: dark)\").matches?\"dark\":\"light\"}if(u)p(u);else try{let n=localStorage.getItem(i)||s,y=l&&n===\"system\"?c():n;p(y)}catch(n){}};var b=[\"light\",\"dark\"],I=\"(prefers-color-scheme: dark)\",O=typeof window==\"undefined\",x=t.createContext(void 0),U={setTheme:e=>{},themes:[]},z=()=>{var e;return(e=t.useContext(x))!=null?e:U},J=e=>t.useContext(x)?t.createElement(t.Fragment,null,e.children):t.createElement(V,{...e}),N=[\"light\",\"dark\"],V=({forcedTheme:e,disableTransitionOnChange:i=!1,enableSystem:s=!0,enableColorScheme:u=!0,storageKey:m=\"theme\",themes:a=N,defaultTheme:l=s?\"system\":\"light\",attribute:h=\"data-theme\",value:d,children:w,nonce:p,scriptProps:R})=>{let[c,n]=t.useState(()=>H(m,l)),[T,y]=t.useState(()=>c===\"system\"?E():c),k=d?Object.values(d):a,S=t.useCallback(o=>{let r=o;if(!r)return;o===\"system\"&&s&&(r=E());let v=d?d[r]:r,C=i?W(p):null,P=document.documentElement,L=g=>{g===\"class\"?(P.classList.remove(...k),v&&P.classList.add(v)):g.startsWith(\"data-\")&&(v?P.setAttribute(g,v):P.removeAttribute(g))};if(Array.isArray(h)?h.forEach(L):L(h),u){let g=b.includes(l)?l:null,D=b.includes(r)?r:g;P.style.colorScheme=D}C==null||C()},[p]),f=t.useCallback(o=>{let r=typeof o==\"function\"?o(c):o;n(r);try{localStorage.setItem(m,r)}catch(v){}},[c]),A=t.useCallback(o=>{let r=E(o);y(r),c===\"system\"&&s&&!e&&S(\"system\")},[c,e]);t.useEffect(()=>{let o=window.matchMedia(I);return o.addListener(A),A(o),()=>o.removeListener(A)},[A]),t.useEffect(()=>{let o=r=>{r.key===m&&(r.newValue?n(r.newValue):f(l))};return window.addEventListener(\"storage\",o),()=>window.removeEventListener(\"storage\",o)},[f]),t.useEffect(()=>{S(e!=null?e:c)},[e,c]);let Q=t.useMemo(()=>({theme:c,setTheme:f,forcedTheme:e,resolvedTheme:c===\"system\"?T:c,themes:s?[...a,\"system\"]:a,systemTheme:s?T:void 0}),[c,f,e,T,s,a]);return t.createElement(x.Provider,{value:Q},t.createElement(_,{forcedTheme:e,storageKey:m,attribute:h,enableSystem:s,enableColorScheme:u,defaultTheme:l,value:d,themes:a,nonce:p,scriptProps:R}),w)},_=t.memo(({forcedTheme:e,storageKey:i,attribute:s,enableSystem:u,enableColorScheme:m,defaultTheme:a,value:l,themes:h,nonce:d,scriptProps:w})=>{let p=JSON.stringify([s,i,a,e,h,l,u,m]).slice(1,-1);return t.createElement(\"script\",{...w,suppressHydrationWarning:!0,nonce:typeof window==\"undefined\"?d:\"\",dangerouslySetInnerHTML:{__html:`(${M.toString()})(${p})`}})}),H=(e,i)=>{if(O)return;let s;try{s=localStorage.getItem(e)||void 0}catch(u){}return s||i},W=e=>{let i=document.createElement(\"style\");return e&&i.setAttribute(\"nonce\",e),i.appendChild(document.createTextNode(\"*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}\")),document.head.appendChild(i),()=>{window.getComputedStyle(document.body),setTimeout(()=>{document.head.removeChild(i)},1)}},E=e=>(e||(e=window.matchMedia(I)),e.matches?\"dark\":\"light\");export{J as ThemeProvider,z as useTheme};\n","\"use client\"\r\n\r\nimport {\r\n CircleCheck,\r\n Info,\r\n LoaderCircle,\r\n OctagonX,\r\n TriangleAlert,\r\n} from \"lucide-react\"\r\nimport { useTheme } from \"next-themes\"\r\nimport { Toaster as Sonner } from \"sonner\"\r\n\r\ntype ToasterProps = React.ComponentProps<typeof Sonner>\r\n\r\ntype Position = ToasterProps[\"position\"]\r\n\r\nconst Toaster = ({ ...props }: ToasterProps) => {\r\n const { theme = \"system\" } = useTheme()\r\n\r\n return (\r\n <Sonner\r\n theme={theme as ToasterProps[\"theme\"]}\r\n className=\"toaster group\"\r\n icons={{\r\n success: <CircleCheck className=\"h-4 w-4\" />,\r\n info: <Info className=\"h-4 w-4\" />,\r\n warning: <TriangleAlert className=\"h-4 w-4\" />,\r\n error: <OctagonX className=\"h-4 w-4\" />,\r\n loading: <LoaderCircle className=\"h-4 w-4 animate-spin\" />,\r\n }}\r\n toastOptions={{\r\n classNames: {\r\n toast:\r\n \"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg\",\r\n description: \"group-[.toast]:text-muted-foreground\",\r\n actionButton:\r\n \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground\",\r\n cancelButton:\r\n \"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground\",\r\n },\r\n }}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport { Toaster }\r\nexport type { Position }\r\n","/**\n * Custom positioning reference element.\n * @see https://floating-ui.com/docs/virtual-elements\n */\n\nconst sides = ['top', 'right', 'bottom', 'left'];\nconst alignments = ['start', 'end'];\nconst placements = /*#__PURE__*/sides.reduce((acc, side) => acc.concat(side, side + \"-\" + alignments[0], side + \"-\" + alignments[1]), []);\nconst min = Math.min;\nconst max = Math.max;\nconst round = Math.round;\nconst floor = Math.floor;\nconst createCoords = v => ({\n x: v,\n y: v\n});\nconst oppositeSideMap = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nconst oppositeAlignmentMap = {\n start: 'end',\n end: 'start'\n};\nfunction clamp(start, value, end) {\n return max(start, min(value, end));\n}\nfunction evaluate(value, param) {\n return typeof value === 'function' ? value(param) : value;\n}\nfunction getSide(placement) {\n return placement.split('-')[0];\n}\nfunction getAlignment(placement) {\n return placement.split('-')[1];\n}\nfunction getOppositeAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}\nfunction getAxisLength(axis) {\n return axis === 'y' ? 'height' : 'width';\n}\nconst yAxisSides = /*#__PURE__*/new Set(['top', 'bottom']);\nfunction getSideAxis(placement) {\n return yAxisSides.has(getSide(placement)) ? 'y' : 'x';\n}\nfunction getAlignmentAxis(placement) {\n return getOppositeAxis(getSideAxis(placement));\n}\nfunction getAlignmentSides(placement, rects, rtl) {\n if (rtl === void 0) {\n rtl = false;\n }\n const alignment = getAlignment(placement);\n const alignmentAxis = getAlignmentAxis(placement);\n const length = getAxisLength(alignmentAxis);\n let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';\n if (rects.reference[length] > rects.floating[length]) {\n mainAlignmentSide = getOppositePlacement(mainAlignmentSide);\n }\n return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];\n}\nfunction getExpandedPlacements(placement) {\n const oppositePlacement = getOppositePlacement(placement);\n return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];\n}\nfunction getOppositeAlignmentPlacement(placement) {\n return placement.replace(/start|end/g, alignment => oppositeAlignmentMap[alignment]);\n}\nconst lrPlacement = ['left', 'right'];\nconst rlPlacement = ['right', 'left'];\nconst tbPlacement = ['top', 'bottom'];\nconst btPlacement = ['bottom', 'top'];\nfunction getSideList(side, isStart, rtl) {\n switch (side) {\n case 'top':\n case 'bottom':\n if (rtl) return isStart ? rlPlacement : lrPlacement;\n return isStart ? lrPlacement : rlPlacement;\n case 'left':\n case 'right':\n return isStart ? tbPlacement : btPlacement;\n default:\n return [];\n }\n}\nfunction getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {\n const alignment = getAlignment(placement);\n let list = getSideList(getSide(placement), direction === 'start', rtl);\n if (alignment) {\n list = list.map(side => side + \"-\" + alignment);\n if (flipAlignment) {\n list = list.concat(list.map(getOppositeAlignmentPlacement));\n }\n }\n return list;\n}\nfunction getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, side => oppositeSideMap[side]);\n}\nfunction expandPaddingObject(padding) {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n ...padding\n };\n}\nfunction getPaddingObject(padding) {\n return typeof padding !== 'number' ? expandPaddingObject(padding) : {\n top: padding,\n right: padding,\n bottom: padding,\n left: padding\n };\n}\nfunction rectToClientRect(rect) {\n const {\n x,\n y,\n width,\n height\n } = rect;\n return {\n width,\n height,\n top: y,\n left: x,\n right: x + width,\n bottom: y + height,\n x,\n y\n };\n}\n\nexport { alignments, clamp, createCoords, evaluate, expandPaddingObject, floor, getAlignment, getAlignmentAxis, getAlignmentSides, getAxisLength, getExpandedPlacements, getOppositeAlignmentPlacement, getOppositeAxis, getOppositeAxisPlacements, getOppositePlacement, getPaddingObject, getSide, getSideAxis, max, min, placements, rectToClientRect, round, sides };\n","import { getSideAxis, getAlignmentAxis, getAxisLength, getSide, getAlignment, evaluate, getPaddingObject, rectToClientRect, min, clamp, placements, getAlignmentSides, getOppositeAlignmentPlacement, getOppositePlacement, getExpandedPlacements, getOppositeAxisPlacements, sides, max, getOppositeAxis } from '@floating-ui/utils';\nexport { rectToClientRect } from '@floating-ui/utils';\n\nfunction computeCoordsFromPlacement(_ref, placement, rtl) {\n let {\n reference,\n floating\n } = _ref;\n const sideAxis = getSideAxis(placement);\n const alignmentAxis = getAlignmentAxis(placement);\n const alignLength = getAxisLength(alignmentAxis);\n const side = getSide(placement);\n const isVertical = sideAxis === 'y';\n const commonX = reference.x + reference.width / 2 - floating.width / 2;\n const commonY = reference.y + reference.height / 2 - floating.height / 2;\n const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;\n let coords;\n switch (side) {\n case 'top':\n coords = {\n x: commonX,\n y: reference.y - floating.height\n };\n break;\n case 'bottom':\n coords = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n case 'right':\n coords = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n case 'left':\n coords = {\n x: reference.x - floating.width,\n y: commonY\n };\n break;\n default:\n coords = {\n x: reference.x,\n y: reference.y\n };\n }\n switch (getAlignment(placement)) {\n case 'start':\n coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);\n break;\n case 'end':\n coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1);\n break;\n }\n return coords;\n}\n\n/**\n * Computes the `x` and `y` coordinates that will place the floating element\n * next to a given reference element.\n *\n * This export does not have any `platform` interface logic. You will need to\n * write one for the platform you are using Floating UI with.\n */\nconst computePosition = async (reference, floating, config) => {\n const {\n placement = 'bottom',\n strategy = 'absolute',\n middleware = [],\n platform\n } = config;\n const validMiddleware = middleware.filter(Boolean);\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));\n let rects = await platform.getElementRects({\n reference,\n floating,\n strategy\n });\n let {\n x,\n y\n } = computeCoordsFromPlacement(rects, placement, rtl);\n let statefulPlacement = placement;\n let middlewareData = {};\n let resetCount = 0;\n for (let i = 0; i < validMiddleware.length; i++) {\n const {\n name,\n fn\n } = validMiddleware[i];\n const {\n x: nextX,\n y: nextY,\n data,\n reset\n } = await fn({\n x,\n y,\n initialPlacement: placement,\n placement: statefulPlacement,\n strategy,\n middlewareData,\n rects,\n platform,\n elements: {\n reference,\n floating\n }\n });\n x = nextX != null ? nextX : x;\n y = nextY != null ? nextY : y;\n middlewareData = {\n ...middlewareData,\n [name]: {\n ...middlewareData[name],\n ...data\n }\n };\n if (reset && resetCount <= 50) {\n resetCount++;\n if (typeof reset === 'object') {\n if (reset.placement) {\n statefulPlacement = reset.placement;\n }\n if (reset.rects) {\n rects = reset.rects === true ? await platform.getElementRects({\n reference,\n floating,\n strategy\n }) : reset.rects;\n }\n ({\n x,\n y\n } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));\n }\n i = -1;\n }\n }\n return {\n x,\n y,\n placement: statefulPlacement,\n strategy,\n middlewareData\n };\n};\n\n/**\n * Resolves with an object of overflow side offsets that determine how much the\n * element is overflowing a given clipping boundary on each side.\n * - positive = overflowing the boundary by that number of pixels\n * - negative = how many pixels left before it will overflow\n * - 0 = lies flush with the boundary\n * @see https://floating-ui.com/docs/detectOverflow\n */\nasync function detectOverflow(state, options) {\n var _await$platform$isEle;\n if (options === void 0) {\n options = {};\n }\n const {\n x,\n y,\n platform,\n rects,\n elements,\n strategy\n } = state;\n const {\n boundary = 'clippingAncestors',\n rootBoundary = 'viewport',\n elementContext = 'floating',\n altBoundary = false,\n padding = 0\n } = evaluate(options, state);\n const paddingObject = getPaddingObject(padding);\n const altContext = elementContext === 'floating' ? 'reference' : 'floating';\n const element = elements[altBoundary ? altContext : elementContext];\n const clippingClientRect = rectToClientRect(await platform.getClippingRect({\n element: ((_await$platform$isEle = await (platform.isElement == null ? void 0 : platform.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || (await (platform.getDocumentElement == null ? void 0 : platform.getDocumentElement(elements.floating))),\n boundary,\n rootBoundary,\n strategy\n }));\n const rect = elementContext === 'floating' ? {\n x,\n y,\n width: rects.floating.width,\n height: rects.floating.height\n } : rects.reference;\n const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));\n const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) ? (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {\n x: 1,\n y: 1\n } : {\n x: 1,\n y: 1\n };\n const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({\n elements,\n rect,\n offsetParent,\n strategy\n }) : rect);\n return {\n top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,\n bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,\n left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,\n right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x\n };\n}\n\n/**\n * Provides data to position an inner element of the floating element so that it\n * appears centered to the reference element.\n * @see https://floating-ui.com/docs/arrow\n */\nconst arrow = options => ({\n name: 'arrow',\n options,\n async fn(state) {\n const {\n x,\n y,\n placement,\n rects,\n platform,\n elements,\n middlewareData\n } = state;\n // Since `element` is required, we don't Partial<> the type.\n const {\n element,\n padding = 0\n } = evaluate(options, state) || {};\n if (element == null) {\n return {};\n }\n const paddingObject = getPaddingObject(padding);\n const coords = {\n x,\n y\n };\n const axis = getAlignmentAxis(placement);\n const length = getAxisLength(axis);\n const arrowDimensions = await platform.getDimensions(element);\n const isYAxis = axis === 'y';\n const minProp = isYAxis ? 'top' : 'left';\n const maxProp = isYAxis ? 'bottom' : 'right';\n const clientProp = isYAxis ? 'clientHeight' : 'clientWidth';\n const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];\n const startDiff = coords[axis] - rects.reference[axis];\n const arrowOffsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));\n let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0;\n\n // DOM platform can return `window` as the `offsetParent`.\n if (!clientSize || !(await (platform.isElement == null ? void 0 : platform.isElement(arrowOffsetParent)))) {\n clientSize = elements.floating[clientProp] || rects.floating[length];\n }\n const centerToReference = endDiff / 2 - startDiff / 2;\n\n // If the padding is large enough that it causes the arrow to no longer be\n // centered, modify the padding so that it is centered.\n const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1;\n const minPadding = min(paddingObject[minProp], largestPossiblePadding);\n const maxPadding = min(paddingObject[maxProp], largestPossiblePadding);\n\n // Make sure the arrow doesn't overflow the floating element if the center\n // point is outside the floating element's bounds.\n const min$1 = minPadding;\n const max = clientSize - arrowDimensions[length] - maxPadding;\n const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;\n const offset = clamp(min$1, center, max);\n\n // If the reference is small enough that the arrow's padding causes it to\n // to point to nothing for an aligned placement, adjust the offset of the\n // floating element itself. To ensure `shift()` continues to take action,\n // a single reset is performed when this is true.\n const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;\n const alignmentOffset = shouldAddOffset ? center < min$1 ? center - min$1 : center - max : 0;\n return {\n [axis]: coords[axis] + alignmentOffset,\n data: {\n [axis]: offset,\n centerOffset: center - offset - alignmentOffset,\n ...(shouldAddOffset && {\n alignmentOffset\n })\n },\n reset: shouldAddOffset\n };\n }\n});\n\nfunction getPlacementList(alignment, autoAlignment, allowedPlacements) {\n const allowedPlacementsSortedByAlignment = alignment ? [...allowedPlacements.filter(placement => getAlignment(placement) === alignment), ...allowedPlacements.filter(placement => getAlignment(placement) !== alignment)] : allowedPlacements.filter(placement => getSide(placement) === placement);\n return allowedPlacementsSortedByAlignment.filter(placement => {\n if (alignment) {\n return getAlignment(placement) === alignment || (autoAlignment ? getOppositeAlignmentPlacement(placement) !== placement : false);\n }\n return true;\n });\n}\n/**\n * Optimizes the visibility of the floating element by choosing the placement\n * that has the most space available automatically, without needing to specify a\n * preferred placement. Alternative to `flip`.\n * @see https://floating-ui.com/docs/autoPlacement\n */\nconst autoPlacement = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'autoPlacement',\n options,\n async fn(state) {\n var _middlewareData$autoP, _middlewareData$autoP2, _placementsThatFitOnE;\n const {\n rects,\n middlewareData,\n placement,\n platform,\n elements\n } = state;\n const {\n crossAxis = false,\n alignment,\n allowedPlacements = placements,\n autoAlignment = true,\n ...detectOverflowOptions\n } = evaluate(options, state);\n const placements$1 = alignment !== undefined || allowedPlacements === placements ? getPlacementList(alignment || null, autoAlignment, allowedPlacements) : allowedPlacements;\n const overflow = await detectOverflow(state, detectOverflowOptions);\n const currentIndex = ((_middlewareData$autoP = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP.index) || 0;\n const currentPlacement = placements$1[currentIndex];\n if (currentPlacement == null) {\n return {};\n }\n const alignmentSides = getAlignmentSides(currentPlacement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating)));\n\n // Make `computeCoords` start from the right place.\n if (placement !== currentPlacement) {\n return {\n reset: {\n placement: placements$1[0]\n }\n };\n }\n const currentOverflows = [overflow[getSide(currentPlacement)], overflow[alignmentSides[0]], overflow[alignmentSides[1]]];\n const allOverflows = [...(((_middlewareData$autoP2 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP2.overflows) || []), {\n placement: currentPlacement,\n overflows: currentOverflows\n }];\n const nextPlacement = placements$1[currentIndex + 1];\n\n // There are more placements to check.\n if (nextPlacement) {\n return {\n data: {\n index: currentIndex + 1,\n overflows: allOverflows\n },\n reset: {\n placement: nextPlacement\n }\n };\n }\n const placementsSortedByMostSpace = allOverflows.map(d => {\n const alignment = getAlignment(d.placement);\n return [d.placement, alignment && crossAxis ?\n // Check along the mainAxis and main crossAxis side.\n d.overflows.slice(0, 2).reduce((acc, v) => acc + v, 0) :\n // Check only the mainAxis.\n d.overflows[0], d.overflows];\n }).sort((a, b) => a[1] - b[1]);\n const placementsThatFitOnEachSide = placementsSortedByMostSpace.filter(d => d[2].slice(0,\n // Aligned placements should not check their opposite crossAxis\n // side.\n getAlignment(d[0]) ? 2 : 3).every(v => v <= 0));\n const resetPlacement = ((_placementsThatFitOnE = placementsThatFitOnEachSide[0]) == null ? void 0 : _placementsThatFitOnE[0]) || placementsSortedByMostSpace[0][0];\n if (resetPlacement !== placement) {\n return {\n data: {\n index: currentIndex + 1,\n overflows: allOverflows\n },\n reset: {\n placement: resetPlacement\n }\n };\n }\n return {};\n }\n };\n};\n\n/**\n * Optimizes the visibility of the floating element by flipping the `placement`\n * in order to keep it in view when the preferred placement(s) will overflow the\n * clipping boundary. Alternative to `autoPlacement`.\n * @see https://floating-ui.com/docs/flip\n */\nconst flip = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'flip',\n options,\n async fn(state) {\n var _middlewareData$arrow, _middlewareData$flip;\n const {\n placement,\n middlewareData,\n rects,\n initialPlacement,\n platform,\n elements\n } = state;\n const {\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = true,\n fallbackPlacements: specifiedFallbackPlacements,\n fallbackStrategy = 'bestFit',\n fallbackAxisSideDirection = 'none',\n flipAlignment = true,\n ...detectOverflowOptions\n } = evaluate(options, state);\n\n // If a reset by the arrow was caused due to an alignment offset being\n // added, we should skip any logic now since `flip()` has already done its\n // work.\n // https://github.com/floating-ui/floating-ui/issues/2549#issuecomment-1719601643\n if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {\n return {};\n }\n const side = getSide(placement);\n const initialSideAxis = getSideAxis(initialPlacement);\n const isBasePlacement = getSide(initialPlacement) === initialPlacement;\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));\n const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));\n const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== 'none';\n if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) {\n fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));\n }\n const placements = [initialPlacement, ...fallbackPlacements];\n const overflow = await detectOverflow(state, detectOverflowOptions);\n const overflows = [];\n let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];\n if (checkMainAxis) {\n overflows.push(overflow[side]);\n }\n if (checkCrossAxis) {\n const sides = getAlignmentSides(placement, rects, rtl);\n overflows.push(overflow[sides[0]], overflow[sides[1]]);\n }\n overflowsData = [...overflowsData, {\n placement,\n overflows\n }];\n\n // One or more sides is overflowing.\n if (!overflows.every(side => side <= 0)) {\n var _middlewareData$flip2, _overflowsData$filter;\n const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;\n const nextPlacement = placements[nextIndex];\n if (nextPlacement) {\n const ignoreCrossAxisOverflow = checkCrossAxis === 'alignment' ? initialSideAxis !== getSideAxis(nextPlacement) : false;\n if (!ignoreCrossAxisOverflow ||\n // We leave the current main axis only if every placement on that axis\n // overflows the main axis.\n overflowsData.every(d => getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true)) {\n // Try next placement and re-run the lifecycle.\n return {\n data: {\n index: nextIndex,\n overflows: overflowsData\n },\n reset: {\n placement: nextPlacement\n }\n };\n }\n }\n\n // First, find the candidates that fit on the mainAxis side of overflow,\n // then find the placement that fits the best on the main crossAxis side.\n let resetPlacement = (_overflowsData$filter = overflowsData.filter(d => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;\n\n // Otherwise fallback.\n if (!resetPlacement) {\n switch (fallbackStrategy) {\n case 'bestFit':\n {\n var _overflowsData$filter2;\n const placement = (_overflowsData$filter2 = overflowsData.filter(d => {\n if (hasFallbackAxisSideDirection) {\n const currentSideAxis = getSideAxis(d.placement);\n return currentSideAxis === initialSideAxis ||\n // Create a bias to the `y` side axis due to horizontal\n // reading directions favoring greater width.\n currentSideAxis === 'y';\n }\n return true;\n }).map(d => [d.placement, d.overflows.filter(overflow => overflow > 0).reduce((acc, overflow) => acc + overflow, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$filter2[0];\n if (placement) {\n resetPlacement = placement;\n }\n break;\n }\n case 'initialPlacement':\n resetPlacement = initialPlacement;\n break;\n }\n }\n if (placement !== resetPlacement) {\n return {\n reset: {\n placement: resetPlacement\n }\n };\n }\n }\n return {};\n }\n };\n};\n\nfunction getSideOffsets(overflow, rect) {\n return {\n top: overflow.top - rect.height,\n right: overflow.right - rect.width,\n bottom: overflow.bottom - rect.height,\n left: overflow.left - rect.width\n };\n}\nfunction isAnySideFullyClipped(overflow) {\n return sides.some(side => overflow[side] >= 0);\n}\n/**\n * Provides data to hide the floating element in applicable situations, such as\n * when it is not in the same clipping context as the reference element.\n * @see https://floating-ui.com/docs/hide\n */\nconst hide = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'hide',\n options,\n async fn(state) {\n const {\n rects\n } = state;\n const {\n strategy = 'referenceHidden',\n ...detectOverflowOptions\n } = evaluate(options, state);\n switch (strategy) {\n case 'referenceHidden':\n {\n const overflow = await detectOverflow(state, {\n ...detectOverflowOptions,\n elementContext: 'reference'\n });\n const offsets = getSideOffsets(overflow, rects.reference);\n return {\n data: {\n referenceHiddenOffsets: offsets,\n referenceHidden: isAnySideFullyClipped(offsets)\n }\n };\n }\n case 'escaped':\n {\n const overflow = await detectOverflow(state, {\n ...detectOverflowOptions,\n altBoundary: true\n });\n const offsets = getSideOffsets(overflow, rects.floating);\n return {\n data: {\n escapedOffsets: offsets,\n escaped: isAnySideFullyClipped(offsets)\n }\n };\n }\n default:\n {\n return {};\n }\n }\n }\n };\n};\n\nfunction getBoundingRect(rects) {\n const minX = min(...rects.map(rect => rect.left));\n const minY = min(...rects.map(rect => rect.top));\n const maxX = max(...rects.map(rect => rect.right));\n const maxY = max(...rects.map(rect => rect.bottom));\n return {\n x: minX,\n y: minY,\n width: maxX - minX,\n height: maxY - minY\n };\n}\nfunction getRectsByLine(rects) {\n const sortedRects = rects.slice().sort((a, b) => a.y - b.y);\n const groups = [];\n let prevRect = null;\n for (let i = 0; i < sortedRects.length; i++) {\n const rect = sortedRects[i];\n if (!prevRect || rect.y - prevRect.y > prevRect.height / 2) {\n groups.push([rect]);\n } else {\n groups[groups.length - 1].push(rect);\n }\n prevRect = rect;\n }\n return groups.map(rect => rectToClientRect(getBoundingRect(rect)));\n}\n/**\n * Provides improved positioning for inline reference elements that can span\n * over multiple lines, such as hyperlinks or range selections.\n * @see https://floating-ui.com/docs/inline\n */\nconst inline = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'inline',\n options,\n async fn(state) {\n const {\n placement,\n elements,\n rects,\n platform,\n strategy\n } = state;\n // A MouseEvent's client{X,Y} coords can be up to 2 pixels off a\n // ClientRect's bounds, despite the event listener being triggered. A\n // padding of 2 seems to handle this issue.\n const {\n padding = 2,\n x,\n y\n } = evaluate(options, state);\n const nativeClientRects = Array.from((await (platform.getClientRects == null ? void 0 : platform.getClientRects(elements.reference))) || []);\n const clientRects = getRectsByLine(nativeClientRects);\n const fallback = rectToClientRect(getBoundingRect(nativeClientRects));\n const paddingObject = getPaddingObject(padding);\n function getBoundingClientRect() {\n // There are two rects and they are disjoined.\n if (clientRects.length === 2 && clientRects[0].left > clientRects[1].right && x != null && y != null) {\n // Find the first rect in which the point is fully inside.\n return clientRects.find(rect => x > rect.left - paddingObject.left && x < rect.right + paddingObject.right && y > rect.top - paddingObject.top && y < rect.bottom + paddingObject.bottom) || fallback;\n }\n\n // There are 2 or more connected rects.\n if (clientRects.length >= 2) {\n if (getSideAxis(placement) === 'y') {\n const firstRect = clientRects[0];\n const lastRect = clientRects[clientRects.length - 1];\n const isTop = getSide(placement) === 'top';\n const top = firstRect.top;\n const bottom = lastRect.bottom;\n const left = isTop ? firstRect.left : lastRect.left;\n const right = isTop ? firstRect.right : lastRect.right;\n const width = right - left;\n const height = bottom - top;\n return {\n top,\n bottom,\n left,\n right,\n width,\n height,\n x: left,\n y: top\n };\n }\n const isLeftSide = getSide(placement) === 'left';\n const maxRight = max(...clientRects.map(rect => rect.right));\n const minLeft = min(...clientRects.map(rect => rect.left));\n const measureRects = clientRects.filter(rect => isLeftSide ? rect.left === minLeft : rect.right === maxRight);\n const top = measureRects[0].top;\n const bottom = measureRects[measureRects.length - 1].bottom;\n const left = minLeft;\n const right = maxRight;\n const width = right - left;\n const height = bottom - top;\n return {\n top,\n bottom,\n left,\n right,\n width,\n height,\n x: left,\n y: top\n };\n }\n return fallback;\n }\n const resetRects = await platform.getElementRects({\n reference: {\n getBoundingClientRect\n },\n floating: elements.floating,\n strategy\n });\n if (rects.reference.x !== resetRects.reference.x || rects.reference.y !== resetRects.reference.y || rects.reference.width !== resetRects.reference.width || rects.reference.height !== resetRects.reference.height) {\n return {\n reset: {\n rects: resetRects\n }\n };\n }\n return {};\n }\n };\n};\n\nconst originSides = /*#__PURE__*/new Set(['left', 'top']);\n\n// For type backwards-compatibility, the `OffsetOptions` type was also\n// Derivable.\n\nasync function convertValueToCoords(state, options) {\n const {\n placement,\n platform,\n elements\n } = state;\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));\n const side = getSide(placement);\n const alignment = getAlignment(placement);\n const isVertical = getSideAxis(placement) === 'y';\n const mainAxisMulti = originSides.has(side) ? -1 : 1;\n const crossAxisMulti = rtl && isVertical ? -1 : 1;\n const rawValue = evaluate(options, state);\n\n // eslint-disable-next-line prefer-const\n let {\n mainAxis,\n crossAxis,\n alignmentAxis\n } = typeof rawValue === 'number' ? {\n mainAxis: rawValue,\n crossAxis: 0,\n alignmentAxis: null\n } : {\n mainAxis: rawValue.mainAxis || 0,\n crossAxis: rawValue.crossAxis || 0,\n alignmentAxis: rawValue.alignmentAxis\n };\n if (alignment && typeof alignmentAxis === 'number') {\n crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;\n }\n return isVertical ? {\n x: crossAxis * crossAxisMulti,\n y: mainAxis * mainAxisMulti\n } : {\n x: mainAxis * mainAxisMulti,\n y: crossAxis * crossAxisMulti\n };\n}\n\n/**\n * Modifies the placement by translating the floating element along the\n * specified axes.\n * A number (shorthand for `mainAxis` or distance), or an axes configuration\n * object may be passed.\n * @see https://floating-ui.com/docs/offset\n */\nconst offset = function (options) {\n if (options === void 0) {\n options = 0;\n }\n return {\n name: 'offset',\n options,\n async fn(state) {\n var _middlewareData$offse, _middlewareData$arrow;\n const {\n x,\n y,\n placement,\n middlewareData\n } = state;\n const diffCoords = await convertValueToCoords(state, options);\n\n // If the placement is the same and the arrow caused an alignment offset\n // then we don't need to change the positioning coordinates.\n if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {\n return {};\n }\n return {\n x: x + diffCoords.x,\n y: y + diffCoords.y,\n data: {\n ...diffCoords,\n placement\n }\n };\n }\n };\n};\n\n/**\n * Optimizes the visibility of the floating element by shifting it in order to\n * keep it in view when it will overflow the clipping boundary.\n * @see https://floating-ui.com/docs/shift\n */\nconst shift = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'shift',\n options,\n async fn(state) {\n const {\n x,\n y,\n placement\n } = state;\n const {\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = false,\n limiter = {\n fn: _ref => {\n let {\n x,\n y\n } = _ref;\n return {\n x,\n y\n };\n }\n },\n ...detectOverflowOptions\n } = evaluate(options, state);\n const coords = {\n x,\n y\n };\n const overflow = await detectOverflow(state, detectOverflowOptions);\n const crossAxis = getSideAxis(getSide(placement));\n const mainAxis = getOppositeAxis(crossAxis);\n let mainAxisCoord = coords[mainAxis];\n let crossAxisCoord = coords[crossAxis];\n if (checkMainAxis) {\n const minSide = mainAxis === 'y' ? 'top' : 'left';\n const maxSide = mainAxis === 'y' ? 'bottom' : 'right';\n const min = mainAxisCoord + overflow[minSide];\n const max = mainAxisCoord - overflow[maxSide];\n mainAxisCoord = clamp(min, mainAxisCoord, max);\n }\n if (checkCrossAxis) {\n const minSide = crossAxis === 'y' ? 'top' : 'left';\n const maxSide = crossAxis === 'y' ? 'bottom' : 'right';\n const min = crossAxisCoord + overflow[minSide];\n const max = crossAxisCoord - overflow[maxSide];\n crossAxisCoord = clamp(min, crossAxisCoord, max);\n }\n const limitedCoords = limiter.fn({\n ...state,\n [mainAxis]: mainAxisCoord,\n [crossAxis]: crossAxisCoord\n });\n return {\n ...limitedCoords,\n data: {\n x: limitedCoords.x - x,\n y: limitedCoords.y - y,\n enabled: {\n [mainAxis]: checkMainAxis,\n [crossAxis]: checkCrossAxis\n }\n }\n };\n }\n };\n};\n/**\n * Built-in `limiter` that will stop `shift()` at a certain point.\n */\nconst limitShift = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n options,\n fn(state) {\n const {\n x,\n y,\n placement,\n rects,\n middlewareData\n } = state;\n const {\n offset = 0,\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = true\n } = evaluate(options, state);\n const coords = {\n x,\n y\n };\n const crossAxis = getSideAxis(placement);\n const mainAxis = getOppositeAxis(crossAxis);\n let mainAxisCoord = coords[mainAxis];\n let crossAxisCoord = coords[crossAxis];\n const rawOffset = evaluate(offset, state);\n const computedOffset = typeof rawOffset === 'number' ? {\n mainAxis: rawOffset,\n crossAxis: 0\n } : {\n mainAxis: 0,\n crossAxis: 0,\n ...rawOffset\n };\n if (checkMainAxis) {\n const len = mainAxis === 'y' ? 'height' : 'width';\n const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;\n const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;\n if (mainAxisCoord < limitMin) {\n mainAxisCoord = limitMin;\n } else if (mainAxisCoord > limitMax) {\n mainAxisCoord = limitMax;\n }\n }\n if (checkCrossAxis) {\n var _middlewareData$offse, _middlewareData$offse2;\n const len = mainAxis === 'y' ? 'width' : 'height';\n const isOriginSide = originSides.has(getSide(placement));\n const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis);\n const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) - (isOriginSide ? computedOffset.crossAxis : 0);\n if (crossAxisCoord < limitMin) {\n crossAxisCoord = limitMin;\n } else if (crossAxisCoord > limitMax) {\n crossAxisCoord = limitMax;\n }\n }\n return {\n [mainAxis]: mainAxisCoord,\n [crossAxis]: crossAxisCoord\n };\n }\n };\n};\n\n/**\n * Provides data that allows you to change the size of the floating element —\n * for instance, prevent it from overflowing the clipping boundary or match the\n * width of the reference element.\n * @see https://floating-ui.com/docs/size\n */\nconst size = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'size',\n options,\n async fn(state) {\n var _state$middlewareData, _state$middlewareData2;\n const {\n placement,\n rects,\n platform,\n elements\n } = state;\n const {\n apply = () => {},\n ...detectOverflowOptions\n } = evaluate(options, state);\n const overflow = await detectOverflow(state, detectOverflowOptions);\n const side = getSide(placement);\n const alignment = getAlignment(placement);\n const isYAxis = getSideAxis(placement) === 'y';\n const {\n width,\n height\n } = rects.floating;\n let heightSide;\n let widthSide;\n if (side === 'top' || side === 'bottom') {\n heightSide = side;\n widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? 'start' : 'end') ? 'left' : 'right';\n } else {\n widthSide = side;\n heightSide = alignment === 'end' ? 'top' : 'bottom';\n }\n const maximumClippingHeight = height - overflow.top - overflow.bottom;\n const maximumClippingWidth = width - overflow.left - overflow.right;\n const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight);\n const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth);\n const noShift = !state.middlewareData.shift;\n let availableHeight = overflowAvailableHeight;\n let availableWidth = overflowAvailableWidth;\n if ((_state$middlewareData = state.middlewareData.shift) != null && _state$middlewareData.enabled.x) {\n availableWidth = maximumClippingWidth;\n }\n if ((_state$middlewareData2 = state.middlewareData.shift) != null && _state$middlewareData2.enabled.y) {\n availableHeight = maximumClippingHeight;\n }\n if (noShift && !alignment) {\n const xMin = max(overflow.left, 0);\n const xMax = max(overflow.right, 0);\n const yMin = max(overflow.top, 0);\n const yMax = max(overflow.bottom, 0);\n if (isYAxis) {\n availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right));\n } else {\n availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom));\n }\n }\n await apply({\n ...state,\n availableWidth,\n availableHeight\n });\n const nextDimensions = await platform.getDimensions(elements.floating);\n if (width !== nextDimensions.width || height !== nextDimensions.height) {\n return {\n reset: {\n rects: true\n }\n };\n }\n return {};\n }\n };\n};\n\nexport { arrow, autoPlacement, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, shift, size };\n","import { rectToClientRect, arrow as arrow$1, autoPlacement as autoPlacement$1, detectOverflow as detectOverflow$1, flip as flip$1, hide as hide$1, inline as inline$1, limitShift as limitShift$1, offset as offset$1, shift as shift$1, size as size$1, computePosition as computePosition$1 } from '@floating-ui/core';\nimport { round, createCoords, max, min, floor } from '@floating-ui/utils';\nimport { getComputedStyle as getComputedStyle$1, isHTMLElement, isElement, getWindow, isWebKit, getFrameElement, getNodeScroll, getDocumentElement, isTopLayer, getNodeName, isOverflowElement, getOverflowAncestors, getParentNode, isLastTraversableNode, isContainingBlock, isTableElement, getContainingBlock } from '@floating-ui/utils/dom';\nexport { getOverflowAncestors } from '@floating-ui/utils/dom';\n\nfunction getCssDimensions(element) {\n const css = getComputedStyle$1(element);\n // In testing environments, the `width` and `height` properties are empty\n // strings for SVG elements, returning NaN. Fallback to `0` in this case.\n let width = parseFloat(css.width) || 0;\n let height = parseFloat(css.height) || 0;\n const hasOffset = isHTMLElement(element);\n const offsetWidth = hasOffset ? element.offsetWidth : width;\n const offsetHeight = hasOffset ? element.offsetHeight : height;\n const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;\n if (shouldFallback) {\n width = offsetWidth;\n height = offsetHeight;\n }\n return {\n width,\n height,\n $: shouldFallback\n };\n}\n\nfunction unwrapElement(element) {\n return !isElement(element) ? element.contextElement : element;\n}\n\nfunction getScale(element) {\n const domElement = unwrapElement(element);\n if (!isHTMLElement(domElement)) {\n return createCoords(1);\n }\n const rect = domElement.getBoundingClientRect();\n const {\n width,\n height,\n $\n } = getCssDimensions(domElement);\n let x = ($ ? round(rect.width) : rect.width) / width;\n let y = ($ ? round(rect.height) : rect.height) / height;\n\n // 0, NaN, or Infinity should always fallback to 1.\n\n if (!x || !Number.isFinite(x)) {\n x = 1;\n }\n if (!y || !Number.isFinite(y)) {\n y = 1;\n }\n return {\n x,\n y\n };\n}\n\nconst noOffsets = /*#__PURE__*/createCoords(0);\nfunction getVisualOffsets(element) {\n const win = getWindow(element);\n if (!isWebKit() || !win.visualViewport) {\n return noOffsets;\n }\n return {\n x: win.visualViewport.offsetLeft,\n y: win.visualViewport.offsetTop\n };\n}\nfunction shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow(element)) {\n return false;\n }\n return isFixed;\n}\n\nfunction getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n const clientRect = element.getBoundingClientRect();\n const domElement = unwrapElement(element);\n let scale = createCoords(1);\n if (includeScale) {\n if (offsetParent) {\n if (isElement(offsetParent)) {\n scale = getScale(offsetParent);\n }\n } else {\n scale = getScale(element);\n }\n }\n const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);\n let x = (clientRect.left + visualOffsets.x) / scale.x;\n let y = (clientRect.top + visualOffsets.y) / scale.y;\n let width = clientRect.width / scale.x;\n let height = clientRect.height / scale.y;\n if (domElement) {\n const win = getWindow(domElement);\n const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;\n let currentWin = win;\n let currentIFrame = getFrameElement(currentWin);\n while (currentIFrame && offsetParent && offsetWin !== currentWin) {\n const iframeScale = getScale(currentIFrame);\n const iframeRect = currentIFrame.getBoundingClientRect();\n const css = getComputedStyle$1(currentIFrame);\n const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;\n const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;\n x *= iframeScale.x;\n y *= iframeScale.y;\n width *= iframeScale.x;\n height *= iframeScale.y;\n x += left;\n y += top;\n currentWin = getWindow(currentIFrame);\n currentIFrame = getFrameElement(currentWin);\n }\n }\n return rectToClientRect({\n width,\n height,\n x,\n y\n });\n}\n\n// If <html> has a CSS width greater than the viewport, then this will be\n// incorrect for RTL.\nfunction getWindowScrollBarX(element, rect) {\n const leftScroll = getNodeScroll(element).scrollLeft;\n if (!rect) {\n return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;\n }\n return rect.left + leftScroll;\n}\n\nfunction getHTMLOffset(documentElement, scroll) {\n const htmlRect = documentElement.getBoundingClientRect();\n const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect);\n const y = htmlRect.top + scroll.scrollTop;\n return {\n x,\n y\n };\n}\n\nfunction convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {\n let {\n elements,\n rect,\n offsetParent,\n strategy\n } = _ref;\n const isFixed = strategy === 'fixed';\n const documentElement = getDocumentElement(offsetParent);\n const topLayer = elements ? isTopLayer(elements.floating) : false;\n if (offsetParent === documentElement || topLayer && isFixed) {\n return rect;\n }\n let scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n let scale = createCoords(1);\n const offsets = createCoords(0);\n const isOffsetParentAnElement = isHTMLElement(offsetParent);\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n if (isHTMLElement(offsetParent)) {\n const offsetRect = getBoundingClientRect(offsetParent);\n scale = getScale(offsetParent);\n offsets.x = offsetRect.x + offsetParent.clientLeft;\n offsets.y = offsetRect.y + offsetParent.clientTop;\n }\n }\n const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);\n return {\n width: rect.width * scale.x,\n height: rect.height * scale.y,\n x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,\n y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y\n };\n}\n\nfunction getClientRects(element) {\n return Array.from(element.getClientRects());\n}\n\n// Gets the entire size of the scrollable document area, even extending outside\n// of the `<html>` and `<body>` rect bounds if horizontally scrollable.\nfunction getDocumentRect(element) {\n const html = getDocumentElement(element);\n const scroll = getNodeScroll(element);\n const body = element.ownerDocument.body;\n const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);\n const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);\n let x = -scroll.scrollLeft + getWindowScrollBarX(element);\n const y = -scroll.scrollTop;\n if (getComputedStyle$1(body).direction === 'rtl') {\n x += max(html.clientWidth, body.clientWidth) - width;\n }\n return {\n width,\n height,\n x,\n y\n };\n}\n\n// Safety check: ensure the scrollbar space is reasonable in case this\n// calculation is affected by unusual styles.\n// Most scrollbars leave 15-18px of space.\nconst SCROLLBAR_MAX = 25;\nfunction getViewportRect(element, strategy) {\n const win = getWindow(element);\n const html = getDocumentElement(element);\n const visualViewport = win.visualViewport;\n let width = html.clientWidth;\n let height = html.clientHeight;\n let x = 0;\n let y = 0;\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n const visualViewportBased = isWebKit();\n if (!visualViewportBased || visualViewportBased && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n const windowScrollbarX = getWindowScrollBarX(html);\n // <html> `overflow: hidden` + `scrollbar-gutter: stable` reduces the\n // visual width of the <html> but this is not considered in the size\n // of `html.clientWidth`.\n if (windowScrollbarX <= 0) {\n const doc = html.ownerDocument;\n const body = doc.body;\n const bodyStyles = getComputedStyle(body);\n const bodyMarginInline = doc.compatMode === 'CSS1Compat' ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0;\n const clippingStableScrollbarWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline);\n if (clippingStableScrollbarWidth <= SCROLLBAR_MAX) {\n width -= clippingStableScrollbarWidth;\n }\n } else if (windowScrollbarX <= SCROLLBAR_MAX) {\n // If the <body> scrollbar is on the left, the width needs to be extended\n // by the scrollbar amount so there isn't extra space on the right.\n width += windowScrollbarX;\n }\n return {\n width,\n height,\n x,\n y\n };\n}\n\nconst absoluteOrFixed = /*#__PURE__*/new Set(['absolute', 'fixed']);\n// Returns the inner client rect, subtracting scrollbars if present.\nfunction getInnerBoundingClientRect(element, strategy) {\n const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');\n const top = clientRect.top + element.clientTop;\n const left = clientRect.left + element.clientLeft;\n const scale = isHTMLElement(element) ? getScale(element) : createCoords(1);\n const width = element.clientWidth * scale.x;\n const height = element.clientHeight * scale.y;\n const x = left * scale.x;\n const y = top * scale.y;\n return {\n width,\n height,\n x,\n y\n };\n}\nfunction getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {\n let rect;\n if (clippingAncestor === 'viewport') {\n rect = getViewportRect(element, strategy);\n } else if (clippingAncestor === 'document') {\n rect = getDocumentRect(getDocumentElement(element));\n } else if (isElement(clippingAncestor)) {\n rect = getInnerBoundingClientRect(clippingAncestor, strategy);\n } else {\n const visualOffsets = getVisualOffsets(element);\n rect = {\n x: clippingAncestor.x - visualOffsets.x,\n y: clippingAncestor.y - visualOffsets.y,\n width: clippingAncestor.width,\n height: clippingAncestor.height\n };\n }\n return rectToClientRect(rect);\n}\nfunction hasFixedPositionAncestor(element, stopNode) {\n const parentNode = getParentNode(element);\n if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {\n return false;\n }\n return getComputedStyle$1(parentNode).position === 'fixed' || hasFixedPositionAncestor(parentNode, stopNode);\n}\n\n// A \"clipping ancestor\" is an `overflow` element with the characteristic of\n// clipping (or hiding) child elements. This returns all clipping ancestors\n// of the given element up the tree.\nfunction getClippingElementAncestors(element, cache) {\n const cachedResult = cache.get(element);\n if (cachedResult) {\n return cachedResult;\n }\n let result = getOverflowAncestors(element, [], false).filter(el => isElement(el) && getNodeName(el) !== 'body');\n let currentContainingBlockComputedStyle = null;\n const elementIsFixed = getComputedStyle$1(element).position === 'fixed';\n let currentNode = elementIsFixed ? getParentNode(element) : element;\n\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {\n const computedStyle = getComputedStyle$1(currentNode);\n const currentNodeIsContaining = isContainingBlock(currentNode);\n if (!currentNodeIsContaining && computedStyle.position === 'fixed') {\n currentContainingBlockComputedStyle = null;\n }\n const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && absoluteOrFixed.has(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);\n if (shouldDropCurrentNode) {\n // Drop non-containing blocks.\n result = result.filter(ancestor => ancestor !== currentNode);\n } else {\n // Record last containing block for next iteration.\n currentContainingBlockComputedStyle = computedStyle;\n }\n currentNode = getParentNode(currentNode);\n }\n cache.set(element, result);\n return result;\n}\n\n// Gets the maximum area that the element is visible in due to any number of\n// clipping ancestors.\nfunction getClippingRect(_ref) {\n let {\n element,\n boundary,\n rootBoundary,\n strategy\n } = _ref;\n const elementClippingAncestors = boundary === 'clippingAncestors' ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary);\n const clippingAncestors = [...elementClippingAncestors, rootBoundary];\n const firstClippingAncestor = clippingAncestors[0];\n const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {\n const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));\n return {\n width: clippingRect.right - clippingRect.left,\n height: clippingRect.bottom - clippingRect.top,\n x: clippingRect.left,\n y: clippingRect.top\n };\n}\n\nfunction getDimensions(element) {\n const {\n width,\n height\n } = getCssDimensions(element);\n return {\n width,\n height\n };\n}\n\nfunction getRectRelativeToOffsetParent(element, offsetParent, strategy) {\n const isOffsetParentAnElement = isHTMLElement(offsetParent);\n const documentElement = getDocumentElement(offsetParent);\n const isFixed = strategy === 'fixed';\n const rect = getBoundingClientRect(element, true, isFixed, offsetParent);\n let scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n const offsets = createCoords(0);\n\n // If the <body> scrollbar appears on the left (e.g. RTL systems). Use\n // Firefox with layout.scrollbar.side = 3 in about:config to test this.\n function setLeftRTLScrollbarOffset() {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n if (isOffsetParentAnElement) {\n const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);\n offsets.x = offsetRect.x + offsetParent.clientLeft;\n offsets.y = offsetRect.y + offsetParent.clientTop;\n } else if (documentElement) {\n setLeftRTLScrollbarOffset();\n }\n }\n if (isFixed && !isOffsetParentAnElement && documentElement) {\n setLeftRTLScrollbarOffset();\n }\n const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);\n const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;\n const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;\n return {\n x,\n y,\n width: rect.width,\n height: rect.height\n };\n}\n\nfunction isStaticPositioned(element) {\n return getComputedStyle$1(element).position === 'static';\n}\n\nfunction getTrueOffsetParent(element, polyfill) {\n if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {\n return null;\n }\n if (polyfill) {\n return polyfill(element);\n }\n let rawOffsetParent = element.offsetParent;\n\n // Firefox returns the <html> element as the offsetParent if it's non-static,\n // while Chrome and Safari return the <body> element. The <body> element must\n // be used to perform the correct calculations even if the <html> element is\n // non-static.\n if (getDocumentElement(element) === rawOffsetParent) {\n rawOffsetParent = rawOffsetParent.ownerDocument.body;\n }\n return rawOffsetParent;\n}\n\n// Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\nfunction getOffsetParent(element, polyfill) {\n const win = getWindow(element);\n if (isTopLayer(element)) {\n return win;\n }\n if (!isHTMLElement(element)) {\n let svgOffsetParent = getParentNode(element);\n while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) {\n if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) {\n return svgOffsetParent;\n }\n svgOffsetParent = getParentNode(svgOffsetParent);\n }\n return win;\n }\n let offsetParent = getTrueOffsetParent(element, polyfill);\n while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) {\n offsetParent = getTrueOffsetParent(offsetParent, polyfill);\n }\n if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) {\n return win;\n }\n return offsetParent || getContainingBlock(element) || win;\n}\n\nconst getElementRects = async function (data) {\n const getOffsetParentFn = this.getOffsetParent || getOffsetParent;\n const getDimensionsFn = this.getDimensions;\n const floatingDimensions = await getDimensionsFn(data.floating);\n return {\n reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy),\n floating: {\n x: 0,\n y: 0,\n width: floatingDimensions.width,\n height: floatingDimensions.height\n }\n };\n};\n\nfunction isRTL(element) {\n return getComputedStyle$1(element).direction === 'rtl';\n}\n\nconst platform = {\n convertOffsetParentRelativeRectToViewportRelativeRect,\n getDocumentElement,\n getClippingRect,\n getOffsetParent,\n getElementRects,\n getClientRects,\n getDimensions,\n getScale,\n isElement,\n isRTL\n};\n\nfunction rectsAreEqual(a, b) {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\n// https://samthor.au/2021/observing-dom/\nfunction observeMove(element, onMove) {\n let io = null;\n let timeoutId;\n const root = getDocumentElement(element);\n function cleanup() {\n var _io;\n clearTimeout(timeoutId);\n (_io = io) == null || _io.disconnect();\n io = null;\n }\n function refresh(skip, threshold) {\n if (skip === void 0) {\n skip = false;\n }\n if (threshold === void 0) {\n threshold = 1;\n }\n cleanup();\n const elementRectForRootMargin = element.getBoundingClientRect();\n const {\n left,\n top,\n width,\n height\n } = elementRectForRootMargin;\n if (!skip) {\n onMove();\n }\n if (!width || !height) {\n return;\n }\n const insetTop = floor(top);\n const insetRight = floor(root.clientWidth - (left + width));\n const insetBottom = floor(root.clientHeight - (top + height));\n const insetLeft = floor(left);\n const rootMargin = -insetTop + \"px \" + -insetRight + \"px \" + -insetBottom + \"px \" + -insetLeft + \"px\";\n const options = {\n rootMargin,\n threshold: max(0, min(1, threshold)) || 1\n };\n let isFirstUpdate = true;\n function handleObserve(entries) {\n const ratio = entries[0].intersectionRatio;\n if (ratio !== threshold) {\n if (!isFirstUpdate) {\n return refresh();\n }\n if (!ratio) {\n // If the reference is clipped, the ratio is 0. Throttle the refresh\n // to prevent an infinite loop of updates.\n timeoutId = setTimeout(() => {\n refresh(false, 1e-7);\n }, 1000);\n } else {\n refresh(false, ratio);\n }\n }\n if (ratio === 1 && !rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) {\n // It's possible that even though the ratio is reported as 1, the\n // element is not actually fully within the IntersectionObserver's root\n // area anymore. This can happen under performance constraints. This may\n // be a bug in the browser's IntersectionObserver implementation. To\n // work around this, we compare the element's bounding rect now with\n // what it was at the time we created the IntersectionObserver. If they\n // are not equal then the element moved, so we refresh.\n refresh();\n }\n isFirstUpdate = false;\n }\n\n // Older browsers don't support a `document` as the root and will throw an\n // error.\n try {\n io = new IntersectionObserver(handleObserve, {\n ...options,\n // Handle <iframe>s\n root: root.ownerDocument\n });\n } catch (_e) {\n io = new IntersectionObserver(handleObserve, options);\n }\n io.observe(element);\n }\n refresh(true);\n return cleanup;\n}\n\n/**\n * Automatically updates the position of the floating element when necessary.\n * Should only be called when the floating element is mounted on the DOM or\n * visible on the screen.\n * @returns cleanup function that should be invoked when the floating element is\n * removed from the DOM or hidden from the screen.\n * @see https://floating-ui.com/docs/autoUpdate\n */\nfunction autoUpdate(reference, floating, update, options) {\n if (options === void 0) {\n options = {};\n }\n const {\n ancestorScroll = true,\n ancestorResize = true,\n elementResize = typeof ResizeObserver === 'function',\n layoutShift = typeof IntersectionObserver === 'function',\n animationFrame = false\n } = options;\n const referenceEl = unwrapElement(reference);\n const ancestors = ancestorScroll || ancestorResize ? [...(referenceEl ? getOverflowAncestors(referenceEl) : []), ...getOverflowAncestors(floating)] : [];\n ancestors.forEach(ancestor => {\n ancestorScroll && ancestor.addEventListener('scroll', update, {\n passive: true\n });\n ancestorResize && ancestor.addEventListener('resize', update);\n });\n const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update) : null;\n let reobserveFrame = -1;\n let resizeObserver = null;\n if (elementResize) {\n resizeObserver = new ResizeObserver(_ref => {\n let [firstEntry] = _ref;\n if (firstEntry && firstEntry.target === referenceEl && resizeObserver) {\n // Prevent update loops when using the `size` middleware.\n // https://github.com/floating-ui/floating-ui/issues/1740\n resizeObserver.unobserve(floating);\n cancelAnimationFrame(reobserveFrame);\n reobserveFrame = requestAnimationFrame(() => {\n var _resizeObserver;\n (_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);\n });\n }\n update();\n });\n if (referenceEl && !animationFrame) {\n resizeObserver.observe(referenceEl);\n }\n resizeObserver.observe(floating);\n }\n let frameId;\n let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;\n if (animationFrame) {\n frameLoop();\n }\n function frameLoop() {\n const nextRefRect = getBoundingClientRect(reference);\n if (prevRefRect && !rectsAreEqual(prevRefRect, nextRefRect)) {\n update();\n }\n prevRefRect = nextRefRect;\n frameId = requestAnimationFrame(frameLoop);\n }\n update();\n return () => {\n var _resizeObserver2;\n ancestors.forEach(ancestor => {\n ancestorScroll && ancestor.removeEventListener('scroll', update);\n ancestorResize && ancestor.removeEventListener('resize', update);\n });\n cleanupIo == null || cleanupIo();\n (_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();\n resizeObserver = null;\n if (animationFrame) {\n cancelAnimationFrame(frameId);\n }\n };\n}\n\n/**\n * Resolves with an object of overflow side offsets that determine how much the\n * element is overflowing a given clipping boundary on each side.\n * - positive = overflowing the boundary by that number of pixels\n * - negative = how many pixels left before it will overflow\n * - 0 = lies flush with the boundary\n * @see https://floating-ui.com/docs/detectOverflow\n */\nconst detectOverflow = detectOverflow$1;\n\n/**\n * Modifies the placement by translating the floating element along the\n * specified axes.\n * A number (shorthand for `mainAxis` or distance), or an axes configuration\n * object may be passed.\n * @see https://floating-ui.com/docs/offset\n */\nconst offset = offset$1;\n\n/**\n * Optimizes the visibility of the floating element by choosing the placement\n * that has the most space available automatically, without needing to specify a\n * preferred placement. Alternative to `flip`.\n * @see https://floating-ui.com/docs/autoPlacement\n */\nconst autoPlacement = autoPlacement$1;\n\n/**\n * Optimizes the visibility of the floating element by shifting it in order to\n * keep it in view when it will overflow the clipping boundary.\n * @see https://floating-ui.com/docs/shift\n */\nconst shift = shift$1;\n\n/**\n * Optimizes the visibility of the floating element by flipping the `placement`\n * in order to keep it in view when the preferred placement(s) will overflow the\n * clipping boundary. Alternative to `autoPlacement`.\n * @see https://floating-ui.com/docs/flip\n */\nconst flip = flip$1;\n\n/**\n * Provides data that allows you to change the size of the floating element —\n * for instance, prevent it from overflowing the clipping boundary or match the\n * width of the reference element.\n * @see https://floating-ui.com/docs/size\n */\nconst size = size$1;\n\n/**\n * Provides data to hide the floating element in applicable situations, such as\n * when it is not in the same clipping context as the reference element.\n * @see https://floating-ui.com/docs/hide\n */\nconst hide = hide$1;\n\n/**\n * Provides data to position an inner element of the floating element so that it\n * appears centered to the reference element.\n * @see https://floating-ui.com/docs/arrow\n */\nconst arrow = arrow$1;\n\n/**\n * Provides improved positioning for inline reference elements that can span\n * over multiple lines, such as hyperlinks or range selections.\n * @see https://floating-ui.com/docs/inline\n */\nconst inline = inline$1;\n\n/**\n * Built-in `limiter` that will stop `shift()` at a certain point.\n */\nconst limitShift = limitShift$1;\n\n/**\n * Computes the `x` and `y` coordinates that will place the floating element\n * next to a given reference element.\n */\nconst computePosition = (reference, floating, options) => {\n // This caches the expensive `getClippingElementAncestors` function so that\n // multiple lifecycle resets re-use the same result. It only lives for a\n // single call. If other functions become expensive, we can add them as well.\n const cache = new Map();\n const mergedOptions = {\n platform,\n ...options\n };\n const platformWithCache = {\n ...mergedOptions.platform,\n _c: cache\n };\n return computePosition$1(reference, floating, {\n ...mergedOptions,\n platform: platformWithCache\n });\n};\n\nexport { arrow, autoPlacement, autoUpdate, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, platform, shift, size };\n","\"use client\";\r\nimport React, { createContext, useContext } from \"react\";\r\nimport { Editor } from \"@tiptap/react\";\r\n\r\nconst EditorRoot = createContext({} as { editor: Editor });\r\n\r\nexport const useCurrentEditor = () => {\r\n const context = useContext(EditorRoot);\r\n if (!context) {\r\n throw new Error(\"useCurrentEditor 必须在 EditorProvider 内部使用\");\r\n }\r\n if (!context.editor) {\r\n throw new Error(\"EditorProvider 未提供 editor\");\r\n }\r\n\r\n return context;\r\n};\r\n\r\nexport const EditorProvider = ({\r\n editor,\r\n children,\r\n}: React.PropsWithChildren<{ editor: Editor }>) => {\r\n if (!editor) return null;\r\n return <EditorRoot.Provider value={{ editor }}>{children}</EditorRoot.Provider>;\r\n};\r\n","import { useEditorState } from \"@tiptap/react\";\r\nimport { useCurrentEditor } from \"../context\";\r\n\r\nexport interface IEditorDerivedState {\r\n // ---- text format ----\r\n isBold: boolean;\r\n canBold: boolean;\r\n isItalic: boolean;\r\n canItalic: boolean;\r\n isUnderline: boolean;\r\n canUnderline: boolean;\r\n isStrike: boolean;\r\n canStrike: boolean;\r\n isCode: boolean;\r\n canCode: boolean;\r\n isHeading1: boolean;\r\n isHeading2: boolean;\r\n isHeading3: boolean;\r\n isHeading4: boolean;\r\n isHeading5: boolean;\r\n isHeading6: boolean;\r\n // ---- node format ----\r\n isParagraph: boolean;\r\n isBulletList: boolean;\r\n isOrderedList: boolean;\r\n isTaskList: boolean;\r\n isBlockquote: boolean;\r\n isCodeBlock: boolean;\r\n // ---- history ----\r\n canUndo: boolean;\r\n canRedo: boolean;\r\n\r\n // ---- align ----\r\n isAlignLeft: boolean;\r\n isAlignRight: boolean;\r\n isAlignCenter: boolean;\r\n isAlignJustify: boolean;\r\n // ---- table ----\r\n isTable: boolean;\r\n canTable: boolean;\r\n}\r\n\r\nexport const useEditorDerivedState = () => {\r\n const { editor } = useCurrentEditor();\r\n\r\n return useEditorState<IEditorDerivedState>({\r\n editor,\r\n selector: (ctx) => {\r\n const { editor } = ctx;\r\n const is = (name: unknown, attrs = {}) =>\r\n editor.isActive(name as unknown as string, attrs) ?? false;\r\n const can = (name: string, attrs = {}) =>\r\n (editor.can().chain() as unknown as any)[name]?.(attrs)?.run();\r\n\r\n return {\r\n // ---- text format ----\r\n isBold: is(\"bold\"),\r\n canBold: can(\"toggleBold\"),\r\n isItalic: is(\"italic\"),\r\n canItalic: can(\"toggleItalic\"),\r\n isUnderline: is(\"underline\"),\r\n canUnderline: can(\"toggleUnderline\"),\r\n isStrike: is(\"strike\"),\r\n canStrike: can(\"toggleStrike\"),\r\n isCode: is(\"code\"),\r\n canCode: can(\"toggleCode\"),\r\n\r\n // ---- node format ----\r\n isParagraph: is(\"paragraph\"),\r\n isHeading1: is(\"heading\", { level: 1 }),\r\n isHeading2: is(\"heading\", { level: 2 }),\r\n isHeading3: is(\"heading\", { level: 3 }),\r\n isHeading4: is(\"heading\", { level: 4 }),\r\n isHeading5: is(\"heading\", { level: 5 }),\r\n isHeading6: is(\"heading\", { level: 6 }),\r\n isBulletList: is(\"bulletList\"),\r\n isOrderedList: is(\"orderedList\"),\r\n isTaskList: is(\"taskList\"),\r\n isBlockquote: is(\"blockquote\"),\r\n isCodeBlock: is(\"codeBlock\"),\r\n\r\n // ---- history ----\r\n canUndo: editor.can().undo(),\r\n canRedo: editor.can().redo(),\r\n\r\n // ---- align ----\r\n isAlignLeft: is({ textAlign: \"left\" }),\r\n isAlignRight: is({ textAlign: \"right\" }),\r\n isAlignCenter: is({ textAlign: \"center\" }),\r\n isAlignJustify: is({ textAlign: \"justify\" }),\r\n\r\n // ---- table ----\r\n isTable: is(\"table\"),\r\n canTable: can(\"insertTable\"),\r\n } as IEditorDerivedState;\r\n },\r\n });\r\n};\r\n","import { clsx, type ClassValue } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs));\r\n}\r\n","\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\n\r\nimport { cn } from \"../../lib/utils\";\r\n\r\nconst toggleVariants = cva(\r\n \"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"bg-transparent\",\r\n outline:\r\n \"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground\",\r\n },\r\n size: {\r\n default: \"h-9 px-2 min-w-9\",\r\n sm: \"h-8 px-1.5 min-w-8\",\r\n lg: \"h-10 px-2.5 min-w-10\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n }\r\n);\r\n\r\ntype ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &\r\n VariantProps<typeof toggleVariants>\r\n\r\nconst Toggle: React.ForwardRefExoticComponent<\r\n ToggleProps & React.RefAttributes<React.ElementRef<typeof TogglePrimitive.Root>>\r\n> = React.forwardRef<\r\n React.ElementRef<typeof TogglePrimitive.Root>,\r\n ToggleProps\r\n>(({ className, variant, size, ...props }, ref) => (\r\n <TogglePrimitive.Root\r\n ref={ref}\r\n data-slot=\"toggle\"\r\n className={cn(toggleVariants({ variant, size, className }))}\r\n {...props}\r\n />\r\n))\r\n\r\nToggle.displayName = TogglePrimitive.Root.displayName\r\n\r\nexport { Toggle, toggleVariants };\r\n","\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nfunction TooltipProvider({\r\n delayDuration = 0,\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\r\n return (\r\n <TooltipPrimitive.Provider\r\n data-slot=\"tooltip-provider\"\r\n delayDuration={delayDuration}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) {\r\n return (\r\n <TooltipProvider>\r\n <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\r\n </TooltipProvider>\r\n );\r\n}\r\n\r\nfunction TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\r\n return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />;\r\n}\r\n\r\nfunction TooltipContent({\r\n className,\r\n sideOffset = 0,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\r\n return (\r\n <TooltipPrimitive.Portal>\r\n <TooltipPrimitive.Content\r\n data-slot=\"tooltip-content\"\r\n sideOffset={sideOffset}\r\n className={cn(\r\n \"origin-(--radix-tooltip-content-transform-origin) z-50 w-fit text-balance rounded-md bg-foreground px-3 py-1.5 text-xs text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground\" />\r\n </TooltipPrimitive.Content>\r\n </TooltipPrimitive.Portal>\r\n );\r\n}\r\n\r\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\r\n","import * as React from \"react\";\r\nimport { Slot } from \"@radix-ui/react-slot\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\n\r\nimport { cn } from \"../../lib/utils\";\r\n\r\nconst buttonVariants = cva(\r\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\r\n destructive:\r\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\r\n outline:\r\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\r\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\r\n ghost: \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\r\n link: \"text-primary underline-offset-4 hover:underline\",\r\n },\r\n size: {\r\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\r\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\r\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\r\n icon: \"size-9\",\r\n \"icon-sm\": \"size-8\",\r\n \"icon-lg\": \"size-10\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n }\r\n);\r\n\r\nconst Button = React.forwardRef<\r\n HTMLButtonElement, // 声明 ref 指向的类型\r\n React.ComponentProps<\"button\"> & VariantProps<typeof buttonVariants> & { asChild?: boolean }\r\n>(({ className, variant = \"default\", size = \"default\", asChild = false, ...props }, ref) => {\r\n const Comp = asChild ? Slot : \"button\";\r\n\r\n return (\r\n <Comp\r\n ref={ref} // 关键:将 ref 转发给真实的 DOM 元素或 Slot\r\n data-slot=\"button\"\r\n data-variant={variant}\r\n data-size={size}\r\n className={cn(buttonVariants({ variant, size, className }))}\r\n {...props}\r\n />\r\n );\r\n});\r\n\r\nButton.displayName = \"Button\";\r\n\r\nexport { Button, buttonVariants };\r\n","import * as React from \"react\";\r\nimport type { TooltipContentProps } from \"@radix-ui/react-tooltip\";\r\nimport { Toggle } from \"../../../components/ui/toggle\";\r\nimport { cn } from \"../../../lib/utils\";\r\nimport {\r\n Tooltip,\r\n TooltipContent,\r\n TooltipProvider,\r\n TooltipTrigger,\r\n} from \"../../../components/ui/tooltip\";\r\nimport { Button } from \"../../../components/ui/button\";\r\n\r\ninterface ToolbarButtonProps extends React.ComponentPropsWithoutRef<typeof Toggle> {\r\n isActive?: boolean;\r\n tooltip?: string;\r\n tooltipOptions?: TooltipContentProps;\r\n className?: string;\r\n shortcutKeys?: string[];\r\n}\r\n\r\nexport const ToolbarButton = React.forwardRef<\r\n HTMLButtonElement,\r\n ToolbarButtonProps & React.PropsWithChildren\r\n>(({ isActive, children, tooltip, className, tooltipOptions, shortcutKeys, ...props }, ref) => {\r\n const toggleButton = (\r\n <Toggle\r\n size=\"sm\"\r\n ref={ref}\r\n className={cn(\r\n \"text-content-first size-7 gap-1 rounded border-none p-0\",\r\n { \"bg-primary/5\": isActive },\r\n className\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n </Toggle>\r\n );\r\n\r\n if (!tooltip) {\r\n return toggleButton;\r\n }\r\n\r\n return (\r\n <TooltipProvider delayDuration={300}>\r\n <Tooltip>\r\n <TooltipTrigger asChild>{toggleButton}</TooltipTrigger>\r\n <TooltipContent {...tooltipOptions}>\r\n <div className=\"flex flex-col items-center text-center\">{tooltip}</div>\r\n </TooltipContent>\r\n </Tooltip>\r\n </TooltipProvider>\r\n );\r\n});\r\n\r\nToolbarButton.displayName = \"ToolbarButton\";\r\n\r\ninterface ActionButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\r\n children: React.ReactNode;\r\n tooltip: string;\r\n tooltipOptions?: TooltipContentProps;\r\n}\r\n\r\nexport const ActionButton = React.memo(\r\n React.forwardRef<HTMLButtonElement, ActionButtonProps>(\r\n ({ children, tooltip, className, ...props }, ref) => (\r\n <TooltipProvider>\r\n <Tooltip>\r\n <TooltipTrigger>\r\n <Button\r\n ref={ref}\r\n variant=\"ghost\"\r\n className={cn(\r\n \"text-content-first relative flex size-7 flex-row rounded p-0\",\r\n \"bg-transparent hover:bg-transparent\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n </Button>\r\n </TooltipTrigger>\r\n <TooltipContent side=\"bottom\">{tooltip}</TooltipContent>\r\n </Tooltip>\r\n </TooltipProvider>\r\n )\r\n )\r\n);\r\n","import {\r\n CodeXml,\r\n Heading1,\r\n Heading2,\r\n Heading3,\r\n Heading4,\r\n Heading5,\r\n Heading6,\r\n List,\r\n ListOrdered,\r\n Quote,\r\n Undo2,\r\n Redo2,\r\n ListTodo,\r\n Type,\r\n type LucideIcon,\r\n AlignLeft,\r\n AlignRight,\r\n AlignCenter,\r\n AlignJustify,\r\n UnderlineIcon,\r\n BoldIcon,\r\n ItalicIcon,\r\n StrikethroughIcon,\r\n Code,\r\n} from \"lucide-react\";\r\nimport { type Editor } from \"@tiptap/react\";\r\nimport { Level } from \"@tiptap/extension-heading\";\r\nimport { type IEditorDerivedState } from \"./hooks/useEditorDerivedState\";\r\n\r\nexport type IActionItem = {\r\n label: string;\r\n icon: LucideIcon;\r\n id: string;\r\n onClick: (editor: Editor) => void;\r\n isActive?: (state: IEditorDerivedState) => boolean;\r\n disabled?: (state: IEditorDerivedState) => boolean;\r\n shortcutKeys?: string[];\r\n};\r\n\r\nconst HeadingIcon = {\r\n 1: Heading1,\r\n 2: Heading2,\r\n 3: Heading3,\r\n 4: Heading4,\r\n 5: Heading5,\r\n 6: Heading6,\r\n};\r\n\r\nexport const getHeadingByLevel = (level: Level): IActionItem => ({\r\n label: `标题${level}`,\r\n icon: HeadingIcon[level],\r\n id: `heading${level}`,\r\n onClick: (editor) => editor.chain().focus().clearNodes().setHeading({ level }).run(),\r\n isActive: (state) => state[`isHeading${level}`],\r\n // shortcutKeys: [\"alt\", \"mod\", `${level}`],\r\n});\r\n\r\nexport const undo: IActionItem = {\r\n label: \"撤销\",\r\n icon: Undo2,\r\n id: \"undo\",\r\n onClick: (editor) => editor.chain().focus().undo().run(),\r\n disabled: (state) => {\r\n return !state.canUndo;\r\n },\r\n shortcutKeys: [\"mod\", \"Z\"],\r\n};\r\n\r\nexport const redo: IActionItem = {\r\n label: \"重做\",\r\n icon: Redo2,\r\n id: \"redo\",\r\n onClick: (editor) => editor.chain().focus().redo().run(),\r\n disabled: (state) => {\r\n return !state.canRedo;\r\n },\r\n shortcutKeys: [\"mod\", \"shift\", \"Z\"],\r\n};\r\n\r\nexport const paragraph: IActionItem = {\r\n label: \"正文\",\r\n icon: Type,\r\n id: \"paragraph\",\r\n onClick: (editor) => editor.chain().focus().clearNodes().setParagraph().run(),\r\n isActive: (state) => state.isParagraph,\r\n};\r\n\r\nexport const blockquote: IActionItem = {\r\n label: \"引用\",\r\n icon: Quote,\r\n id: \"blockquote\",\r\n onClick: (editor) => editor.chain().focus().clearNodes().setBlockquote().run(),\r\n isActive: (state) => state.isBlockquote,\r\n // shortcutKeys: [\"shift\", \"mod\", \"B\"],\r\n};\r\n\r\nexport const codeBlock: IActionItem = {\r\n label: \"代码块\",\r\n icon: CodeXml,\r\n id: \"codeBlock\",\r\n onClick: (editor) => editor.chain().focus().clearNodes().setCodeBlock().run(),\r\n isActive: (state) => state.isCodeBlock,\r\n};\r\n\r\nexport const orderedList: IActionItem = {\r\n label: \"有序列表\",\r\n icon: ListOrdered,\r\n id: \"orderedList\",\r\n onClick: (editor) => editor.chain().focus().clearNodes().toggleOrderedList().run(),\r\n isActive: (state) => state.isOrderedList,\r\n};\r\n\r\nexport const bulletList: IActionItem = {\r\n label: \"无序列表\",\r\n icon: List,\r\n id: \"bulletList\",\r\n onClick: (editor) => editor.chain().focus().clearNodes().toggleBulletList().run(),\r\n isActive: (state) => state.isBulletList,\r\n};\r\n\r\nexport const todoList: IActionItem = {\r\n label: \"待办任务\",\r\n icon: ListTodo,\r\n id: \"todoList\",\r\n onClick: (editor) => editor.chain().focus().clearNodes().toggleTaskList().run(),\r\n isActive: (editor) => editor.isTaskList,\r\n // shortcutKeys: [\"shift\", \"mod\", \"9\"],\r\n};\r\n\r\nexport const toolbarNodeConfig = {\r\n select: [\r\n getHeadingByLevel(1),\r\n getHeadingByLevel(2),\r\n getHeadingByLevel(3),\r\n getHeadingByLevel(4),\r\n getHeadingByLevel(5),\r\n paragraph,\r\n blockquote,\r\n codeBlock,\r\n ],\r\n out: [bulletList, orderedList, todoList],\r\n};\r\n\r\nexport const leftAlign: IActionItem = {\r\n label: \"左对齐\",\r\n icon: AlignLeft,\r\n id: \"leftAlign\",\r\n onClick: (editor) => editor.chain().setTextAlign(\"left\").run(),\r\n isActive: (state) => state.isAlignLeft,\r\n};\r\nexport const rightAlign: IActionItem = {\r\n label: \"右对齐\",\r\n icon: AlignRight,\r\n id: \"rightAlign\",\r\n onClick: (editor) => editor.chain().setTextAlign(\"right\").run(),\r\n isActive: (state) => state.isAlignRight,\r\n};\r\nexport const centerAlign: IActionItem = {\r\n label: \"居中对齐\",\r\n icon: AlignCenter,\r\n id: \"centerAlign\",\r\n onClick: (editor) => editor.chain().setTextAlign(\"center\").run(),\r\n isActive: (state) => state.isAlignCenter,\r\n};\r\nexport const justifyAlign: IActionItem = {\r\n label: \"两端对齐\",\r\n icon: AlignJustify,\r\n id: \"justifyAlign\",\r\n onClick: (editor) => editor.chain().setTextAlign(\"justify\").run(),\r\n isActive: (state) => state.isAlignJustify,\r\n};\r\n\r\nexport const textAlignActions: IActionItem[] = [leftAlign, rightAlign, centerAlign, justifyAlign];\r\n\r\nexport const bold: IActionItem = {\r\n label: \"加粗\",\r\n icon: BoldIcon,\r\n id: \"bold\",\r\n onClick: (editor) => editor.chain().focus().toggleBold().run(),\r\n isActive: (editor) => editor.isBold,\r\n disabled: (state) => !state.canBold,\r\n shortcutKeys: [\"mod\", \"B\"],\r\n};\r\n\r\nexport const italic: IActionItem = {\r\n label: \"斜体\",\r\n icon: ItalicIcon,\r\n id: \"italic\",\r\n onClick: (editor) => editor.chain().focus().toggleItalic().run(),\r\n isActive: (editor) => editor.isItalic,\r\n disabled: (state) => !state.canItalic,\r\n shortcutKeys: [\"mod\", \"I\"],\r\n};\r\n\r\nexport const underline: IActionItem = {\r\n label: \"下划线\",\r\n icon: UnderlineIcon,\r\n id: \"underline\",\r\n onClick: (editor) => editor.chain().focus().toggleUnderline().run(),\r\n isActive: (editor) => editor.isUnderline,\r\n disabled: (state) => !state.canUnderline,\r\n shortcutKeys: [\"mod\", \"U\"],\r\n};\r\n\r\nexport const strike: IActionItem = {\r\n label: \"删除线\",\r\n icon: StrikethroughIcon,\r\n id: \"strike\",\r\n onClick: (editor) => editor.chain().focus().toggleStrike().run(),\r\n isActive: (editor) => editor.isStrike,\r\n disabled: (state) => !state.canStrike,\r\n shortcutKeys: [\"mod\", \"shift\", \"S\"],\r\n};\r\n\r\nexport const inlineCode: IActionItem = {\r\n label: \"行内代码\",\r\n icon: Code,\r\n id: \"inlineCode\",\r\n onClick: (editor) => editor.chain().focus().toggleCode().run(),\r\n isActive: (editor) => editor.isCode,\r\n disabled: (state) => !state.canCode,\r\n shortcutKeys: [\"mod\", \"E\"],\r\n};\r\n\r\nexport const textFormatActions: IActionItem[] = [bold, italic, underline, strike, inlineCode];\r\n\r\nexport const nodeFormatActions: IActionItem[] = [\r\n getHeadingByLevel(1),\r\n getHeadingByLevel(2),\r\n getHeadingByLevel(3),\r\n getHeadingByLevel(4),\r\n getHeadingByLevel(5),\r\n paragraph,\r\n blockquote,\r\n codeBlock,\r\n bulletList,\r\n orderedList,\r\n todoList,\r\n];\r\n","import { useCurrentEditor } from \"../../context\";\r\nimport { useEditorDerivedState } from \"../../hooks/useEditorDerivedState\";\r\nimport { ToolbarButton } from \"../../toolbar/components/action-button\";\r\nimport { nodeFormatActions } from \"../../toolbarAction\";\r\n\r\nconst Menus = ({ currentNode, currentNodePos }: any) => {\r\n const { editor } = useCurrentEditor();\r\n\r\n const state = useEditorDerivedState();\r\n\r\n return (\r\n <div className={\"gap flex flex-wrap gap-[12px]\"}>\r\n {nodeFormatActions.map((item) => (\r\n <ToolbarButton\r\n key={item.id}\r\n isActive={item.isActive?.(state)}\r\n onClick={() => item.onClick(editor)}\r\n disabled={item.disabled?.(state)}\r\n tooltip={item.label}\r\n shortcutKeys={item.shortcutKeys}\r\n >\r\n <item.icon size={20} />\r\n </ToolbarButton>\r\n ))}\r\n </div>\r\n );\r\n};\r\n\r\nexport default Menus;\r\n","\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\r\nimport { CheckIcon, ChevronRightIcon, CircleIcon } from \"lucide-react\";\r\n\r\nimport { cn } from \"../../lib/utils\";\r\n\r\nfunction DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\r\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuPortal({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\r\n return <DropdownMenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuTrigger({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\r\n return <DropdownMenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuContent({\r\n className,\r\n sideOffset = 4,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\r\n return (\r\n <DropdownMenuPrimitive.Portal>\r\n <DropdownMenuPrimitive.Content\r\n data-slot=\"dropdown-menu-content\"\r\n sideOffset={sideOffset}\r\n className={cn(\r\n \"max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin) z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n </DropdownMenuPrimitive.Portal>\r\n );\r\n}\r\n\r\nfunction DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\r\n return <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuItem({\r\n className,\r\n inset,\r\n variant = \"default\",\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\r\n inset?: boolean;\r\n variant?: \"default\" | \"destructive\";\r\n}) {\r\n return (\r\n <DropdownMenuPrimitive.Item\r\n data-slot=\"dropdown-menu-item\"\r\n data-inset={inset}\r\n data-variant={variant}\r\n className={cn(\r\n \"data-[variant=destructive]:*:[svg]:!text-destructive outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[disabled]:opacity-50 data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuCheckboxItem({\r\n className,\r\n children,\r\n checked,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {\r\n return (\r\n <DropdownMenuPrimitive.CheckboxItem\r\n data-slot=\"dropdown-menu-checkbox-item\"\r\n className={cn(\r\n \"outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n className\r\n )}\r\n checked={checked}\r\n {...props}\r\n >\r\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\r\n <DropdownMenuPrimitive.ItemIndicator>\r\n <CheckIcon className=\"size-4\" />\r\n </DropdownMenuPrimitive.ItemIndicator>\r\n </span>\r\n {children}\r\n </DropdownMenuPrimitive.CheckboxItem>\r\n );\r\n}\r\n\r\nfunction DropdownMenuRadioGroup({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\r\n return <DropdownMenuPrimitive.RadioGroup data-slot=\"dropdown-menu-radio-group\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuRadioItem({\r\n className,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {\r\n return (\r\n <DropdownMenuPrimitive.RadioItem\r\n data-slot=\"dropdown-menu-radio-item\"\r\n className={cn(\r\n \"outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\r\n <DropdownMenuPrimitive.ItemIndicator>\r\n <CircleIcon className=\"size-2 fill-current\" />\r\n </DropdownMenuPrimitive.ItemIndicator>\r\n </span>\r\n {children}\r\n </DropdownMenuPrimitive.RadioItem>\r\n );\r\n}\r\n\r\nfunction DropdownMenuLabel({\r\n className,\r\n inset,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\r\n inset?: boolean;\r\n}) {\r\n return (\r\n <DropdownMenuPrimitive.Label\r\n data-slot=\"dropdown-menu-label\"\r\n data-inset={inset}\r\n className={cn(\"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8\", className)}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuSeparator({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\r\n return (\r\n <DropdownMenuPrimitive.Separator\r\n data-slot=\"dropdown-menu-separator\"\r\n className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\r\n return (\r\n <span\r\n data-slot=\"dropdown-menu-shortcut\"\r\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\r\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuSubTrigger({\r\n className,\r\n inset,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\r\n inset?: boolean;\r\n}) {\r\n return (\r\n <DropdownMenuPrimitive.SubTrigger\r\n data-slot=\"dropdown-menu-sub-trigger\"\r\n data-inset={inset}\r\n className={cn(\r\n \"outline-hidden flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[inset]:pl-8 data-[state=open]:text-accent-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <ChevronRightIcon className=\"ml-auto size-4\" />\r\n </DropdownMenuPrimitive.SubTrigger>\r\n );\r\n}\r\n\r\nfunction DropdownMenuSubContent({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\r\n return (\r\n <DropdownMenuPrimitive.SubContent\r\n data-slot=\"dropdown-menu-sub-content\"\r\n className={cn(\r\n \"origin-(--radix-dropdown-menu-content-transform-origin) z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nexport {\r\n DropdownMenu,\r\n DropdownMenuPortal,\r\n DropdownMenuTrigger,\r\n DropdownMenuContent,\r\n DropdownMenuGroup,\r\n DropdownMenuLabel,\r\n DropdownMenuItem,\r\n DropdownMenuCheckboxItem,\r\n DropdownMenuRadioGroup,\r\n DropdownMenuRadioItem,\r\n DropdownMenuSeparator,\r\n DropdownMenuShortcut,\r\n DropdownMenuSub,\r\n DropdownMenuSubTrigger,\r\n DropdownMenuSubContent,\r\n};\r\n","import { flip, offset, shift } from \"@floating-ui/dom\"\nimport {\n\tautoUpdate,\n\tflip as flipReact,\n\toffset as offsetReact,\n\tshift as shiftReact,\n\tuseClick,\n\tuseDismiss,\n\tuseFloating,\n\tuseInteractions,\n} from \"@floating-ui/react\"\nimport { DragHandle } from \"@tiptap/extension-drag-handle-react\"\nimport copyTextToClipboard from \"copy-to-clipboard\"\nimport { Copy, GripVertical, Trash2 } from \"lucide-react\"\nimport type { Node as TiptapNode } from \"prosemirror-model\"\nimport React, { useCallback, useState } from \"react\"\nimport Menus from \"@/bubble-menu/bubble-menu-block/menus\"\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from \"@/components/ui/dropdown-menu\"\nimport { useCurrentEditor } from \"@/context\"\n\nconst BubbleMenuBlock = () => {\n\tconst { editor } = useCurrentEditor()\n\n\t// 检查 editor 是否存在且未被销毁\n\tif (!editor || editor.isDestroyed) return null\n\n\tconst [isOpen, setIsOpen] = useState(false)\n\n\tconst [node, setNode] = useState<TiptapNode | null>(null)\n\tconst [nodePos, setNodePos] = useState<number>(-1)\n\n\tconst handleNodeChange = useCallback((data: any) => {\n\t\tif (data.node) setNode(data.node)\n\t\tsetNodePos(data.pos)\n\t}, [])\n\n\tconst { refs, floatingStyles, context } = useFloating({\n\t\topen: isOpen,\n\t\tonOpenChange: setIsOpen,\n\t\tplacement: \"right-start\",\n\t\twhileElementsMounted: autoUpdate,\n\t\tmiddleware: [offsetReact(8), flipReact(), shiftReact()],\n\t})\n\n\tconst click = useClick(context)\n\tconst dismiss = useDismiss(context)\n\tconst { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss])\n\tconst copyToClipboard = () => {\n\t\teditor.chain().setMeta(\"hideDragHandle\", true).run()\n\t\teditor.chain().setNodeSelection(nodePos).run()\n\t\tconst text = node?.textContent\n\t\tconsole.log(text)\n\t\tcopyTextToClipboard(text || \"\")\n\t}\n\n\tconst removeNode = () => {\n\t\teditor.chain().setMeta(\"hideDragHandle\", true).setNodeSelection(nodePos).deleteSelection().run()\n\t}\n\treturn (\n\t\t<DropdownMenu>\n\t\t\t<DragHandle\n\t\t\t\tpluginKey=\"block-drag-handle\"\n\t\t\t\teditor={editor}\n\t\t\t\tonNodeChange={handleNodeChange}\n\t\t\t\tclassName=\"transition-all duration-200 ease-in-out z-50 will-change-transform\"\n\t\t\t\tcomputePositionConfig={{\n\t\t\t\t\tmiddleware: [\n\t\t\t\t\t\toffset(10), // 手柄离开文字的距离\n\t\t\t\t\t\tflip(),\n\t\t\t\t\t\tshift({ padding: 5 }),\n\t\t\t\t\t],\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<DropdownMenuTrigger asChild>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={`\n p-0.5 rounded-md cursor-grab active:cursor-grabbing transition-colors\n ${isOpen ? \"bg-blue-100 text-blue-600\" : \"hover:bg-gray-100 text-gray-400 hover:text-gray-600\"}\n `}\n\t\t\t\t\t>\n\t\t\t\t\t\t<GripVertical size={18} />\n\t\t\t\t\t</div>\n\t\t\t\t</DropdownMenuTrigger>\n\t\t\t</DragHandle>\n\n\t\t\t<DropdownMenuContent\n\t\t\t\tclassName=\"z-50 w-[164px] border-none p-[8px] shadow-[0_0px_12px_0px_rgba(0,0,0,0.1)]\"\n\t\t\t\talign=\"start\"\n\t\t\t\tside=\"bottom\"\n\t\t\t\tsideOffset={0}\n\t\t\t>\n\t\t\t\t{[\"image\", \"table\"].indexOf(node?.type.name || \"\") === -1 && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<Menus currentNode={node} currentNodePos={nodePos} />\n\t\t\t\t\t\t<DropdownMenuSeparator />\n\t\t\t\t\t\t<DropdownMenuItem className=\"flex cursor-pointer gap-3\" onClick={copyToClipboard}>\n\t\t\t\t\t\t\t<Copy size={16} />\n\t\t\t\t\t\t\t<span>复制</span>\n\t\t\t\t\t\t</DropdownMenuItem>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\n\t\t\t\t<DropdownMenuItem onClick={removeNode} className=\"flex cursor-pointer gap-3\">\n\t\t\t\t\t<Trash2 size={16} />\n\t\t\t\t\t<span>删除</span>\n\t\t\t\t</DropdownMenuItem>\n\t\t\t</DropdownMenuContent>\n\t\t</DropdownMenu>\n\t)\n}\n\nexport default BubbleMenuBlock\n","import { findParentNode, isTextSelection } from \"@tiptap/core\";\r\nimport { Editor } from \"@tiptap/react\";\r\nimport { CellSelection, Rect, TableMap } from \"@tiptap/pm/tables\";\r\nimport { Selection, Transaction } from \"@tiptap/pm/state\";\r\n\r\n// 定义自定义节点类型数组,这些节点在选中时需要特殊处理\r\nconst customNodes = [\"image\", \"codeBlock\", \"link\", \"table\", \"title\"];\r\n\r\nexport const isTableGripSelected = (node: HTMLElement) => {\r\n let container = node;\r\n\r\n while (container && ![\"TD\", \"TH\"].includes(container.tagName)) {\r\n container = container.parentElement!;\r\n }\r\n\r\n const gripColumn =\r\n container && container.querySelector && container.querySelector(\"a.grip-column.selected\");\r\n const gripRow =\r\n container && container.querySelector && container.querySelector(\"a.grip-row.selected\");\r\n\r\n return !!(gripColumn || gripRow);\r\n};\r\n\r\n/**\r\n * 检查是否选中了自定义节点(如图片、代码块、链接、表格、标题等)\r\n * @param editor - Tiptap 编辑器实例\r\n * @param node - 当前的 HTML 元素节点\r\n * @returns 如果选中了自定义节点则返回 true\r\n */\r\nexport const isCustomNodeSelected = (editor: Editor, node: HTMLElement) => {\r\n // 从编辑器的选区状态中获取选区的起始位置(from)和结束位置(to)\r\n const { from, to } = editor.state.selection;\r\n // 初始化标题节点标志为 false\r\n let hasTitle = false;\r\n\r\n // 遍历选区范围内的所有节点\r\n editor.state.doc.nodesBetween(from, to, (node, pos) => {\r\n // 检查节点类型是否为 title(标题节点)\r\n if (node.type.name === \"title\") {\r\n // 如果是标题节点,将标志设为 true\r\n hasTitle = true;\r\n }\r\n });\r\n\r\n // 检查是否有任何自定义节点类型处于激活状态(使用 some 方法遍历 customNodes 数组)\r\n const hasCustomNode = customNodes.some((type) => editor.isActive(type));\r\n // 检查表格的拖拽手柄是否被选中\r\n const hasTableGrip = isTableGripSelected(node);\r\n // 如果有自定义节点被激活、或表格拖拽手柄被选中、或存在标题节点,则返回 true\r\n return hasCustomNode || hasTableGrip || hasTitle;\r\n};\r\n\r\n/**\r\n * 检查编辑器中是否选中了文本内容\r\n * @param editor - Tiptap 编辑器实例\r\n * @returns 如果选中了有效的文本内容则返回 true\r\n */\r\nexport const isTextSelected = ({ editor }: { editor: Editor }) => {\r\n // 从编辑器状态中解构出文档、选区对象,以及选区的 empty(是否为空)、from(起始位置)、to(结束位置)属性\r\n const {\r\n doc,\r\n selection,\r\n selection: { empty, from, to },\r\n } = editor.state;\r\n\r\n // 判断是否为空文本块:如果选区范围内没有文本内容且当前选区是文本选区类型\r\n const isEmptyTextBlock = !doc.textBetween(from, to).length && isTextSelection(selection);\r\n\r\n // 返回 true 的条件:选区不为空 && 不是空文本块 && 编辑器可编辑\r\n // 使用取反操作符,如果任何一个条件为 true 则返回 false\r\n return !(empty || isEmptyTextBlock || !editor.isEditable);\r\n};\r\n\r\n/**\r\n * 在当前选区中查找表格节点\r\n * @param selection - ProseMirror 选区对象\r\n * @returns 找到的表格节点,如果没有则返回 undefined\r\n */\r\nexport const findTable = (selection: Selection) =>\r\n // 使用 findParentNode 工具函数查找父节点\r\n findParentNode(\r\n // 判断条件:节点的 spec.tableRole 属性存在且值为 \"table\"\r\n (node) => node.type.spec.tableRole && node.type.spec.tableRole === \"table\"\r\n )(selection); // 传入选区对象执行查找\r\n\r\n/**\r\n * 检查指定的矩形区域是否被完全选中\r\n * @param rect - 矩形区域对象,包含 left、right、top、bottom 属性\r\n * @returns 返回一个函数,该函数接收选区对象并判断矩形区域是否被选中\r\n */\r\nexport const isRectSelected = (rect: Rect) => (selection: Selection) => {\r\n // 检查当前选区是否为单元格选区类型\r\n if (isCellSelection(selection)) {\r\n // 获取锚点单元格所在表格的映射对象(-1 表示向上查找一级父节点,即表格节点)\r\n const map = TableMap.get(selection.$anchorCell.node(-1));\r\n // 获取锚点单元格在表格中的起始位置\r\n const start = selection.$anchorCell.start(-1);\r\n // 获取指定矩形区域内的所有单元格索引\r\n const cells = map.cellsInRect(rect);\r\n // 获取当前实际选中的单元格索引(通过计算锚点和头部单元格之间的矩形区域)\r\n const selectedCells = map.cellsInRect(\r\n map.rectBetween(\r\n // 锚点单元格的相对位置(相对于表格起始位置)\r\n selection.$anchorCell.pos - start,\r\n // 头部单元格的相对位置(相对于表格起始位置)\r\n selection.$headCell.pos - start\r\n )\r\n );\r\n\r\n // 遍历矩形区域内的所有单元格\r\n for (let i = 0, count = cells.length; i < count; i += 1) {\r\n // 如果某个单元格不在实际选中的单元格列表中\r\n if (selectedCells.indexOf(cells[i]) === -1) {\r\n // 返回 false,表示矩形区域未被完全选中\r\n return false;\r\n }\r\n }\r\n\r\n // 所有单元格都被选中,返回 true\r\n return true;\r\n }\r\n // 如果不是单元格选区,返回 false\r\n return false;\r\n};\r\n\r\n/**\r\n * 类型守卫函数:检查选区是否为单元格选区类型\r\n * @param selection - ProseMirror 选区对象\r\n * @returns 如果是单元格选区则返回 true,并将类型缩窄为 CellSelection\r\n */\r\nexport const isCellSelection = (selection: Selection): selection is CellSelection =>\r\n selection instanceof CellSelection; // 使用 instanceof 检查选区是否为 CellSelection 实例\r\n\r\n/**\r\n * 检查指定列是否被完全选中\r\n * @param columnIndex - 列索引(从 0 开始)\r\n * @returns 返回一个函数,该函数接收选区对象并判断列是否被选中\r\n */\r\nexport const isColumnSelected = (columnIndex: number) => (selection: Selection) => {\r\n // 检查当前选区是否为单元格选区类型\r\n if (isCellSelection(selection)) {\r\n // 获取锚点单元格所在表格的映射对象\r\n const map = TableMap.get(selection.$anchorCell.node(-1));\r\n\r\n // 调用 isRectSelected 检查整列是否被选中\r\n return isRectSelected({\r\n left: columnIndex, // 矩形左边界为目标列索引\r\n right: columnIndex + 1, // 矩形右边界为目标列索引 + 1(表示单列)\r\n top: 0, // 矩形顶部从第一行开始\r\n bottom: map.height, // 矩形底部到最后一行(覆盖整列)\r\n })(selection);\r\n }\r\n\r\n // 如果不是单元格选区,返回 false\r\n return false;\r\n};\r\n\r\n/**\r\n * 检查指定行是否被完全选中\r\n * @param rowIndex - 行索引(从 0 开始)\r\n * @returns 返回一个函数,该函数接收选区对象并判断行是否被选中\r\n */\r\nexport const isRowSelected = (rowIndex: number) => (selection: Selection) => {\r\n // 检查当前选区是否为单元格选区类型\r\n if (isCellSelection(selection)) {\r\n // 获取锚点单元格所在表格的映射对象\r\n const map = TableMap.get(selection.$anchorCell.node(-1));\r\n\r\n // 调用 isRectSelected 检查整行是否被选中\r\n return isRectSelected({\r\n left: 0, // 矩形左边界从第一列开始\r\n right: map.width, // 矩形右边界到最后一列(覆盖整行)\r\n top: rowIndex, // 矩形顶部为目标行索引\r\n bottom: rowIndex + 1, // 矩形底部为目标行索引 + 1(表示单行)\r\n })(selection);\r\n }\r\n\r\n // 如果不是单元格选区,返回 false\r\n return false;\r\n};\r\n\r\n/**\r\n * 检查整个表格是否被完全选中\r\n * @param selection - ProseMirror 选区对象\r\n * @returns 如果整个表格被选中则返回 true\r\n */\r\nexport const isTableSelected = (selection: Selection) => {\r\n // 检查当前选区是否为单元格选区类型\r\n if (isCellSelection(selection)) {\r\n // 获取锚点单元格所在表格的映射对象\r\n const map = TableMap.get(selection.$anchorCell.node(-1));\r\n\r\n // 调用 isRectSelected 检查整个表格是否被选中\r\n return isRectSelected({\r\n left: 0, // 矩形左边界从第一列开始\r\n right: map.width, // 矩形右边界到最后一列\r\n top: 0, // 矩形顶部从第一行开始\r\n bottom: map.height, // 矩形底部到最后一行(覆盖整个表格)\r\n })(selection);\r\n }\r\n\r\n // 如果不是单元格选区,返回 false\r\n return false;\r\n};\r\n\r\n/**\r\n * 获取指定列中的所有单元格信息\r\n * @param columnIndex - 列索引(可以是单个数字或数字数组)\r\n * @returns 返回一个函数,该函数接收选区对象并返回单元格信息数组\r\n */\r\nexport const getCellsInColumn = (columnIndex: number | number[]) => (selection: Selection) => {\r\n // 在当前选区中查找表格节点\r\n const table = findTable(selection);\r\n if (table) {\r\n // 获取表格的映射对象\r\n const map = TableMap.get(table.node);\r\n // 将列索引统一转换为数组格式(如果传入的是单个数字,则转为包含该数字的数组)\r\n const indexes = Array.isArray(columnIndex) ? columnIndex : Array.from([columnIndex]);\r\n\r\n // 使用 reduce 遍历所有列索引,收集单元格信息\r\n return indexes.reduce(\r\n (acc, index) => {\r\n // 检查列索引是否有效(在 0 到表格宽度-1 之间)\r\n if (index >= 0 && index <= map.width - 1) {\r\n // 获取该列中所有单元格的位置索引\r\n const cells = map.cellsInRect({\r\n left: index, // 列的左边界\r\n right: index + 1, // 列的右边界(单列)\r\n top: 0, // 从第一行开始\r\n bottom: map.height, // 到最后一行(整列)\r\n });\r\n\r\n // 将新的单元格信息合并到累加器中\r\n return acc.concat(\r\n // 将每个单元格位置转换为包含节点信息的对象\r\n cells.map((nodePos) => {\r\n // 获取该位置的节点对象\r\n const node = table.node.nodeAt(nodePos) as any;\r\n // 计算节点在文档中的绝对位置(节点相对位置 + 表格起始位置)\r\n const pos = nodePos + table.start;\r\n\r\n // 返回单元格信息对象:pos(位置)、start(内容起始位置)、node(节点对象)\r\n return { pos, start: pos + 1, node };\r\n })\r\n );\r\n }\r\n\r\n // 如果索引无效,返回原累加器\r\n return acc;\r\n },\r\n // 初始值为空数组,类型为包含 pos、start、node 属性的对象数组\r\n [] as { pos: number; start: number; node: Node | null | undefined }[]\r\n );\r\n }\r\n // 如果没有找到表格,返回 null\r\n return null;\r\n};\r\n\r\n/**\r\n * 获取指定行中的所有单元格信息\r\n * @param rowIndex - 行索引(可以是单个数字或数字数组)\r\n * @returns 返回一个函数,该函数接收选区对象并返回单元格信息数组\r\n */\r\nexport const getCellsInRow = (rowIndex: number | number[]) => (selection: Selection) => {\r\n // 在当前选区中查找表格节点\r\n const table = findTable(selection);\r\n\r\n if (table) {\r\n // 获取表格的映射对象\r\n const map = TableMap.get(table.node);\r\n // 将行索引统一转换为数组格式(如果传入的是单个数字,则转为包含该数字的数组)\r\n const indexes = Array.isArray(rowIndex) ? rowIndex : Array.from([rowIndex]);\r\n\r\n // 使用 reduce 遍历所有行索引,收集单元格信息\r\n return indexes.reduce(\r\n (acc, index) => {\r\n // 检查行索引是否有效(在 0 到表格高度-1 之间)\r\n if (index >= 0 && index <= map.height - 1) {\r\n // 获取该行中所有单元格的位置索引\r\n const cells = map.cellsInRect({\r\n left: 0, // 从第一列开始\r\n right: map.width, // 到最后一列(整行)\r\n top: index, // 行的顶部边界\r\n bottom: index + 1, // 行的底部边界(单行)\r\n });\r\n\r\n // 将新的单元格信息合并到累加器中\r\n return acc.concat(\r\n // 将每个单元格位置转换为包含节点信息的对象\r\n cells.map((nodePos) => {\r\n // 获取该位置的节点对象\r\n const node = table.node.nodeAt(nodePos) as any;\r\n // 计算节点在文档中的绝对位置(节点相对位置 + 表格起始位置)\r\n const pos = nodePos + table.start;\r\n // 返回单元格信息对象:pos(位置)、start(内容起始位置)、node(节点对象)\r\n return { pos, start: pos + 1, node };\r\n })\r\n );\r\n }\r\n\r\n // 如果索引无效,返回原累加器\r\n return acc;\r\n },\r\n // 初始值为空数组,类型为包含 pos、start、node 属性的对象数组\r\n [] as { pos: number; start: number; node: Node | null | undefined }[]\r\n );\r\n }\r\n\r\n // 如果没有找到表格,返回 null\r\n return null;\r\n};\r\n\r\nconst select = (type: \"row\" | \"column\") => (index: number) => (tr: Transaction) => {\r\n const table = findTable(tr.selection);\r\n const isRowSelection = type === \"row\";\r\n\r\n if (table) {\r\n const map = TableMap.get(table.node);\r\n\r\n // Check if the index is valid\r\n if (index >= 0 && index < (isRowSelection ? map.height : map.width)) {\r\n const left = isRowSelection ? 0 : index;\r\n const top = isRowSelection ? index : 0;\r\n const right = isRowSelection ? map.width : index + 1;\r\n const bottom = isRowSelection ? index + 1 : map.height;\r\n\r\n const cellsInFirstRow = map.cellsInRect({\r\n left,\r\n top,\r\n right: isRowSelection ? right : left + 1,\r\n bottom: isRowSelection ? top + 1 : bottom,\r\n });\r\n\r\n const cellsInLastRow =\r\n bottom - top === 1\r\n ? cellsInFirstRow\r\n : map.cellsInRect({\r\n left: isRowSelection ? left : right - 1,\r\n top: isRowSelection ? bottom - 1 : top,\r\n right,\r\n bottom,\r\n });\r\n\r\n const head = table.start + cellsInFirstRow[0];\r\n const anchor = table.start + cellsInLastRow[cellsInLastRow.length - 1];\r\n const $head = tr.doc.resolve(head);\r\n const $anchor = tr.doc.resolve(anchor);\r\n\r\n return tr.setSelection(new CellSelection($anchor, $head));\r\n }\r\n }\r\n return tr;\r\n};\r\n\r\nexport const selectColumn = select(\"column\");\r\n\r\nexport const selectRow = select(\"row\");\r\n","export const TABLE_INIT_GRID_SIZE = 10;\r\nexport const TABLE_MAX_GRID_SIZE = 10;\r\nexport const TABLE_MAX_ROW_SIZE = 100;\r\nexport const TABLE_MAX_COLUMN_SIZE = 50;\r\nexport const TABLE_DEFAULT_SELECTED_GRID_SIZE = 2;\r\n\r\nexport const IMAGE_MAX_SIZE = 10;\r\nexport const IMAGE_MAX_WIDTH = 1000;\r\nexport const IMAGE_MAX_HEIGHT = 840;\r\nexport const IMAGE_MIN_HEIGHT = 120;\r\nexport const IMAGE_MIN_WIDTH = 120;\r\nexport const IMAGE_THROTTLE_WAIT_TIME = 16;\r\n","import { memo, useCallback } from \"react\";\r\nimport { BubbleMenu } from \"@tiptap/react/menus\";\r\nimport {\r\n ArrowLeftToLine,\r\n ArrowRightToLine,\r\n ArrowUpToLine,\r\n ArrowDownToLine,\r\n TableCellsMerge,\r\n TableCellsSplit,\r\n Trash2,\r\n} from \"lucide-react\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\nimport { useCurrentEditor } from \"../../context\";\r\nimport {\r\n getCellsInColumn,\r\n getCellsInRow,\r\n isCellSelection,\r\n isColumnSelected,\r\n isRowSelected,\r\n isTableSelected,\r\n} from \"../../extensions/extension-table/utils\";\r\nimport { TABLE_MAX_COLUMN_SIZE, TABLE_MAX_ROW_SIZE } from \"../../utils/constant\";\r\nimport { ToolbarButton } from \"../../toolbar/components/action-button\";\r\n\r\nfunction BubbleMenuTable() {\r\n const { editor } = useCurrentEditor();\r\n\r\n const selectorState = useEditorState({\r\n editor,\r\n selector: (ctx) => {\r\n const { selection, doc } = ctx.editor.state;\r\n const colCells = getCellsInColumn(0)(selection);\r\n const rowCells = getCellsInRow(0)(selection);\r\n let rowCount = 0;\r\n let columnCount = 0;\r\n\r\n doc.nodesBetween(selection.from, selection.to, (node) => {\r\n // 只关注块级节点\r\n if (node.type.name === \"table\") {\r\n rowCount = node.childCount;\r\n columnCount = node.firstChild ? node.firstChild.childCount : 0;\r\n }\r\n });\r\n\r\n return {\r\n canAddColumnBefore: ctx.editor.can().addColumnBefore(),\r\n canAddColumnAfter: ctx.editor.can().addColumnAfter(),\r\n canDeleteColumn: ctx.editor.can().deleteColumn(),\r\n canAddRowBefore: ctx.editor.can().addRowBefore(),\r\n canAddRowAfter: ctx.editor.can().addRowAfter(),\r\n canDeleteRow: ctx.editor.can().deleteRow(),\r\n canSplitCell: ctx.editor.can().splitCell(),\r\n canMergeCell: ctx.editor.can().mergeCells(),\r\n canDeleteTable: ctx.editor.can().deleteTable(),\r\n isRowGripSelected: colCells?.some((_, index) => isRowSelected(index)(selection)),\r\n isColumnGripSelected: rowCells?.some((_, index) => isColumnSelected(index)(selection)),\r\n rowCount,\r\n columnCount,\r\n };\r\n },\r\n });\r\n\r\n const shouldShow = useCallback(({ editor, state, view, from }: any) => {\r\n if (!state || !from) {\r\n return false;\r\n }\r\n\r\n const domAtPos = view.domAtPos(from).node as HTMLElement;\r\n const nodeDOM = view.nodeDOM(from) as HTMLElement;\r\n const node = nodeDOM || domAtPos;\r\n if (!editor.isActive(\"table\") || !node || isTableSelected(state.selection)) {\r\n return false;\r\n }\r\n return isCellSelection(state.selection);\r\n }, []);\r\n\r\n const onAddColumn = (type: \"before\" | \"after\") => {\r\n console.log(\"onAddColumn selectorState.columnCount\", selectorState.columnCount);\r\n if (selectorState.columnCount >= TABLE_MAX_COLUMN_SIZE) {\r\n // toast.warning(\r\n // `表格列数已达上限(${TABLE_MAX_COLUMN_SIZE}),请插入新的表格以继续编辑`,\r\n // )\r\n return;\r\n }\r\n type === \"before\"\r\n ? editor.chain().focus().addColumnBefore().run()\r\n : editor.chain().focus().addColumnAfter().run();\r\n };\r\n\r\n const onAddRow = (type: \"before\" | \"after\") => {\r\n console.log(\"onAddRow selectorState.rowCount\", selectorState.rowCount);\r\n if (selectorState.rowCount >= TABLE_MAX_ROW_SIZE) {\r\n // toast.error(\r\n // `表格行数已达上限(${TABLE_MAX_ROW_SIZE}),请插入新的表格继续编辑`,\r\n // )\r\n return;\r\n }\r\n type === \"before\"\r\n ? editor.chain().focus().addRowBefore().run()\r\n : editor.chain().focus().addRowAfter().run();\r\n };\r\n\r\n return (\r\n <BubbleMenu\r\n editor={editor}\r\n pluginKey=\"tableBubbleMenu\"\r\n shouldShow={shouldShow}\r\n updateDelay={0}\r\n className=\"z-20\"\r\n options={{\r\n offset: {\r\n mainAxis: 0,\r\n crossAxis: 8,\r\n },\r\n placement: \"top-start\",\r\n onShow: () => {},\r\n onHide: () => {\r\n console.log(\"tableBubbleMenu onHidden\");\r\n },\r\n }}\r\n >\r\n <div className=\"border-line z-50 flex w-full items-center gap-3 rounded-xl border bg-white px-3 py-2 shadow-[0px_0px_20px_0px_rgba(0,0,0,0.1)]\">\r\n {selectorState.isColumnGripSelected && (\r\n <ToolbarButton\r\n tooltip=\"在前面插入列\"\r\n onClick={() => {\r\n onAddColumn(\"before\");\r\n }}\r\n tooltip-options={{\r\n sideOffset: 15,\r\n }}\r\n disabled={!selectorState.canAddColumnBefore}\r\n >\r\n <ArrowLeftToLine size={20} />\r\n </ToolbarButton>\r\n )}\r\n {selectorState.isColumnGripSelected && (\r\n <ToolbarButton\r\n tooltip=\"在后面插入列\"\r\n onClick={() => {\r\n onAddColumn(\"after\");\r\n }}\r\n tooltip-options={{\r\n sideOffset: 15,\r\n }}\r\n disabled={!selectorState.canAddColumnAfter}\r\n >\r\n <ArrowRightToLine size={20} />\r\n </ToolbarButton>\r\n )}\r\n {selectorState.isColumnGripSelected && (\r\n <ToolbarButton\r\n tooltip=\"删除列\"\r\n onClick={() => {\r\n editor.chain().focus().deleteColumn().run();\r\n }}\r\n tooltip-options={{\r\n sideOffset: 15,\r\n }}\r\n disabled={!selectorState.canDeleteColumn}\r\n >\r\n <Trash2 size={20} />\r\n </ToolbarButton>\r\n )}\r\n {selectorState.isRowGripSelected && (\r\n <ToolbarButton\r\n onClick={() => {\r\n onAddRow(\"before\");\r\n }}\r\n tooltip=\"在上面插入行\"\r\n tooltip-options={{\r\n sideOffset: 15,\r\n }}\r\n disabled={!selectorState.canAddRowBefore}\r\n >\r\n <ArrowUpToLine size={20} />\r\n </ToolbarButton>\r\n )}\r\n {selectorState.isRowGripSelected && (\r\n <ToolbarButton\r\n onClick={() => {\r\n onAddRow(\"after\");\r\n }}\r\n tooltip=\"在下面插入行\"\r\n tooltip-options={{\r\n sideOffset: 15,\r\n }}\r\n disabled={!selectorState.canAddRowBefore}\r\n >\r\n <ArrowDownToLine size={20} />\r\n </ToolbarButton>\r\n )}\r\n {selectorState.isRowGripSelected && (\r\n <ToolbarButton\r\n onClick={() => {\r\n editor.chain().focus().deleteRow().run();\r\n }}\r\n tooltip=\"删除行\"\r\n tooltip-options={{\r\n sideOffset: 15,\r\n }}\r\n disabled={!selectorState.canDeleteRow}\r\n >\r\n <Trash2 size={20} />\r\n </ToolbarButton>\r\n )}\r\n <ToolbarButton\r\n onClick={() => {\r\n editor.chain().focus().mergeCells().run();\r\n }}\r\n tooltip=\"合并单元格\"\r\n tooltip-options={{\r\n sideOffset: 15,\r\n }}\r\n disabled={!selectorState.canMergeCell}\r\n >\r\n <TableCellsMerge size={20} />\r\n </ToolbarButton>\r\n\r\n <ToolbarButton\r\n onClick={() => {\r\n editor.chain().focus().splitCell().run();\r\n }}\r\n tooltip=\"拆分单元格\"\r\n tooltip-options={{\r\n sideOffset: 15,\r\n }}\r\n disabled={!selectorState.canSplitCell}\r\n >\r\n <TableCellsSplit size={20} />\r\n </ToolbarButton>\r\n </div>\r\n </BubbleMenu>\r\n );\r\n}\r\n\r\nexport default memo(BubbleMenuTable);\r\n","\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\";\r\n\r\nimport { cn } from \"../../lib/utils\";\r\n\r\nfunction Separator({\r\n className,\r\n orientation = \"horizontal\",\r\n decorative = true,\r\n ...props\r\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\r\n return (\r\n <SeparatorPrimitive.Root\r\n data-slot=\"separator\"\r\n decorative={decorative}\r\n orientation={orientation}\r\n className={cn(\r\n \"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=vertical]:h-full data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nexport { Separator };\r\n","import { memo } from \"react\";\r\nimport { useCurrentEditor } from \"../../../context\";\r\nimport { IActionItem } from \"../../../toolbarAction\";\r\nimport { ToolbarButton } from \"../action-button\";\r\nimport type { TooltipContentProps } from \"@radix-ui/react-tooltip\";\r\nimport { useEditorDerivedState } from \"../../../hooks/useEditorDerivedState\";\r\n\r\ninterface IProps {\r\n textFormatActions: IActionItem[];\r\n tooltipOptions?: TooltipContentProps;\r\n}\r\n\r\nconst TextSelector = ({ textFormatActions, tooltipOptions }: IProps) => {\r\n const { editor } = useCurrentEditor();\r\n const state = useEditorDerivedState();\r\n\r\n return (\r\n <>\r\n {textFormatActions.map((item) => (\r\n <ToolbarButton\r\n key={item.id}\r\n isActive={item.isActive?.(state)}\r\n onClick={() => item.onClick(editor)}\r\n disabled={item.disabled?.(state)}\r\n tooltip={item.label}\r\n tooltipOptions={tooltipOptions}\r\n shortcutKeys={item.shortcutKeys}\r\n >\r\n <item.icon size={20} />\r\n </ToolbarButton>\r\n ))}\r\n </>\r\n );\r\n};\r\n\r\nexport default memo(TextSelector);\r\n","\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\";\r\n\r\nimport { cn } from \"../../lib/utils\";\r\n\r\nfunction Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {\r\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />;\r\n}\r\n\r\nfunction PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\r\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\r\n}\r\n\r\nfunction PopoverContent({\r\n className,\r\n align = \"center\",\r\n sideOffset = 4,\r\n ...props\r\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\r\n return (\r\n <PopoverPrimitive.Portal>\r\n <PopoverPrimitive.Content\r\n data-slot=\"popover-content\"\r\n align={align}\r\n sideOffset={sideOffset}\r\n className={cn(\r\n \"origin-(--radix-popover-content-transform-origin) outline-hidden z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n </PopoverPrimitive.Portal>\r\n );\r\n}\r\n\r\nfunction PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\r\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />;\r\n}\r\n\r\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };\r\n","\"use client\";\r\n\r\nimport * as React from \"react\";\r\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nfunction Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {\r\n return (\r\n <LabelPrimitive.Root\r\n data-slot=\"label\"\r\n className={cn(\r\n \"flex select-none items-center gap-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nexport { Label };\r\n","import * as React from \"react\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\r\n return (\r\n <input\r\n type={type}\r\n data-slot=\"input\"\r\n className={cn(\r\n \"shadow-xs h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30\",\r\n \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\r\n \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nexport { Input };\r\n","import * as React from \"react\";\r\nimport { Button } from \"../../../components/ui/button\";\r\nimport { Label } from \"../../../components/ui/label\";\r\nimport { Input } from \"../../../components/ui/input\";\r\nimport { cn } from \"../../../lib/utils\";\r\n\r\nexport interface LinkEditorProps extends React.HTMLAttributes<HTMLDivElement> {\r\n defaultUrl?: string;\r\n defaultText?: string;\r\n onSave: (url: string, text?: string, isNewTab?: boolean) => void;\r\n}\r\n\r\nexport const LinkEditPanel = React.forwardRef<HTMLDivElement, LinkEditorProps>(\r\n ({ onSave, defaultUrl, defaultText, className }, ref) => {\r\n const formRef = React.useRef<HTMLDivElement>(null);\r\n const [url, setUrl] = React.useState(defaultUrl || \"\");\r\n const [text, setText] = React.useState(defaultText || \"\");\r\n\r\n const handleSave = React.useCallback(\r\n (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (formRef.current) {\r\n const isValid = Array.from(formRef.current.querySelectorAll(\"input\")).every((input) =>\r\n input.checkValidity()\r\n );\r\n\r\n if (isValid) {\r\n onSave(url, text);\r\n } else {\r\n formRef.current.querySelectorAll(\"input\").forEach((input) => {\r\n if (!input.checkValidity()) {\r\n input.reportValidity();\r\n }\r\n });\r\n }\r\n }\r\n },\r\n [onSave, url, text]\r\n );\r\n\r\n React.useImperativeHandle(ref, () => formRef.current as HTMLDivElement);\r\n\r\n return (\r\n <div className=\"shadow-box w-full min-w-80 rounded-xl p-4\" ref={formRef}>\r\n <div className={cn(\"space-y-4\", className)}>\r\n <div className=\"space-y-1\">\r\n <Label>链接</Label>\r\n <Input\r\n type=\"url\"\r\n required\r\n placeholder=\"输入链接\"\r\n value={url}\r\n onChange={(e) => setUrl(e.target.value)}\r\n onKeyDown={(e) => {\r\n if (e.key === \"Enter\") {\r\n handleSave(e); // 按回车键时触发保存\r\n }\r\n }}\r\n />\r\n </div>\r\n\r\n <div className=\"space-y-1\">\r\n <Label>文本</Label>\r\n <Input\r\n type=\"text\"\r\n placeholder=\"输入文本\"\r\n value={text}\r\n onChange={(e) => setText(e.target.value)}\r\n onKeyDown={(e) => {\r\n if (e.key === \"Enter\") {\r\n handleSave(e); // 按回车键时触发保存\r\n }\r\n }}\r\n />\r\n </div>\r\n\r\n <div className=\"flex justify-end space-x-2\">\r\n <Button type=\"button\" onClick={handleSave}>\r\n 保存\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n);\r\n\r\nLinkEditPanel.displayName = \"LinkEditPanel\";\r\n","\"use client\";\r\nimport React, { useCallback } from \"react\";\r\nimport { LinkIcon } from \"lucide-react\";\r\nimport { ToolbarButton } from \"../action-button\";\r\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../../components/ui/popover\";\r\nimport { LinkEditPanel } from \"../../../extensions/extension-link/widget/edit-panel\";\r\nimport { cn } from \"../../../lib/utils\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\nimport { useCurrentEditor } from \"../../../context\";\r\nimport { Button } from \"../../../components/ui/button\";\r\n\r\nconst LinkSelector = ({ tooltipOptions }: { tooltipOptions?: any }) => {\r\n const { editor } = useCurrentEditor();\r\n\r\n const { href, text, isActive } = useEditorState({\r\n editor,\r\n selector: (ctx) => {\r\n const { from, to } = ctx.editor.state.selection;\r\n const { href } = ctx.editor.getAttributes(\"link\");\r\n const text = editor.state.doc.textBetween(from, to, \" \");\r\n\r\n return {\r\n href,\r\n text,\r\n isActive: editor.isActive(\"link\"),\r\n };\r\n },\r\n });\r\n\r\n const onSetLink = useCallback(\r\n (href: string, text?: string) => {\r\n editor\r\n .chain()\r\n .extendMarkRange(\"link\")\r\n .insertContent({\r\n type: \"text\",\r\n text,\r\n marks: [\r\n {\r\n type: \"link\",\r\n attrs: {\r\n href,\r\n target: \"_blank\",\r\n },\r\n },\r\n ],\r\n })\r\n .setLink({ href, target: \"_blank\" })\r\n .focus()\r\n .run();\r\n },\r\n [editor]\r\n );\r\n\r\n return (\r\n <Popover defaultOpen={isActive}>\r\n <PopoverTrigger asChild>\r\n <ToolbarButton\r\n pressed={isActive}\r\n isActive={isActive}\r\n tooltip=\"插入链接\"\r\n tooltipOptions={tooltipOptions}\r\n >\r\n <LinkIcon size={19} />\r\n </ToolbarButton>\r\n </PopoverTrigger>\r\n <PopoverContent align=\"center\" className=\"z-50 w-max\" sideOffset={18}>\r\n <LinkEditPanel defaultUrl={href} defaultText={text} onSave={onSetLink} />\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n};\r\n\r\nexport default LinkSelector;\r\n","import { Baseline, ChevronDown } from \"lucide-react\";\r\nimport * as Popover from \"@radix-ui/react-popover\";\r\nimport { cn } from \"../../../lib/utils\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\nimport { useCurrentEditor } from \"../../../context\";\r\nimport { Button } from \"../../../components/ui/button\";\r\n\r\nconst TEXT_COLORS: string[] = [\r\n \"#111111\",\r\n \"#414141\",\r\n \"#707070\",\r\n \"#B8B8B8\",\r\n \"#E7E7E7\",\r\n \"#FFFFFF\",\r\n \"#4D0000\",\r\n \"#800000\",\r\n \"#B30000\",\r\n \"#FF0000\",\r\n \"#FF4C4C\",\r\n \"#FFB2B2\",\r\n \"#4D2D16\",\r\n \"#804B25\",\r\n \"#B36934\",\r\n \"#FF964A\",\r\n \"#FFB580\",\r\n \"#FFDFC9\",\r\n \"#4D4110\",\r\n \"#806D1B\",\r\n \"#B39926\",\r\n \"#FFDA36\",\r\n \"#FFE572\",\r\n \"#FFF4C3\",\r\n \"#084623\",\r\n \"#0D7539\",\r\n \"#12A451\",\r\n \"#19EA73\",\r\n \"#5EF09D\",\r\n \"#BAF9D5\",\r\n \"#003845\",\r\n \"#005E72\",\r\n \"#0083A0\",\r\n \"#00BBE5\",\r\n \"#4CCFED\",\r\n \"#B2EBF7\",\r\n \"#021C4D\",\r\n \"#032F80\",\r\n \"#0542B3\",\r\n \"#075EFF\",\r\n \"#518EFF\",\r\n \"#B5CFFF\",\r\n \"#36204D\",\r\n \"#5A3580\",\r\n \"#7E4AB3\",\r\n \"#B46AFF\",\r\n \"#CA97FF\",\r\n \"#E8D2FF\",\r\n\r\n // \"#111111\",\r\n // \"#707070\",\r\n // \"#B8B8B8\",\r\n // \"#B30000\",\r\n // \"#B36934\",\r\n // \"#B39926\",\r\n // \"#12A451\",\r\n // \"#0083A0\",\r\n // \"#0542B3\",\r\n // \"#7E4AB3\",\r\n];\r\n\r\nconst ColorSelector = () => {\r\n const { editor } = useCurrentEditor();\r\n\r\n const { activeColorItem } = useEditorState({\r\n editor,\r\n selector: ({ editor }) => {\r\n return {\r\n activeColorItem: TEXT_COLORS.find((color) => editor.isActive(\"textStyle\", { color })),\r\n };\r\n },\r\n });\r\n\r\n return (\r\n <Popover.Root>\r\n <Popover.Trigger asChild>\r\n <Button variant=\"ghost\" className=\"h-7 gap-1 rounded px-1\">\r\n <Baseline size={20} color={activeColorItem} />\r\n <ChevronDown size={12} color=\"#9fa2ab\" />\r\n </Button>\r\n </Popover.Trigger>\r\n\r\n <Popover.Content\r\n className=\"border-line shadow-box z-50 w-[242px] rounded-xl border bg-white p-4\"\r\n align=\"center\"\r\n sideOffset={18}\r\n >\r\n <div>\r\n <div\r\n className=\"text-content-first border-line mb-[10px] flex h-8 cursor-pointer items-center justify-center rounded-lg border text-sm\"\r\n onClick={() => {\r\n editor.chain().focus().unsetColor().run();\r\n }}\r\n >\r\n 默认颜色\r\n </div>\r\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\r\n {TEXT_COLORS.map((color: string) => (\r\n <span\r\n key={color}\r\n className={cn(\r\n \"hover:border-content-first flex h-7 w-7 cursor-pointer items-center justify-center rounded-sm border border-transparent\",\r\n {\r\n \"border-content-first\": activeColorItem === color,\r\n }\r\n )}\r\n onClick={() => {\r\n editor.chain().focus().unsetColor().run();\r\n editor.chain().focus().setColor(color).run();\r\n }}\r\n >\r\n <span\r\n className=\"border-line h-6 w-6 rounded-sm border\"\r\n style={{\r\n backgroundColor: color,\r\n }}\r\n />\r\n </span>\r\n ))}\r\n </div>\r\n </div>\r\n </Popover.Content>\r\n </Popover.Root>\r\n );\r\n};\r\n\r\nexport default ColorSelector;\r\n","import { ChevronDown, Highlighter } from \"lucide-react\";\r\nimport * as Popover from \"@radix-ui/react-popover\";\r\n\r\nimport { useEditorState } from \"@tiptap/react\";\r\nimport { useCurrentEditor } from \"../../../context\";\r\nimport { Button } from \"../../../components/ui/button\";\r\nimport { cn } from \"../../../lib/utils\";\r\n\r\nconst HIGHLIGHT_COLORS: string[] = [\r\n \"#FF0000\",\r\n \"#FF4C4C\",\r\n \"#FFB2B2\",\r\n \"#FF964A\",\r\n \"#FFB580\",\r\n \"#FFDFC9\",\r\n \"#FFDA36\",\r\n \"#FFE572\",\r\n \"#FFF4C3\",\r\n \"#19EA73\",\r\n \"#5EF09D\",\r\n \"#BAF9D5\",\r\n \"#00BBE5\",\r\n \"#4CCFED\",\r\n \"#B2EBF7\",\r\n \"#075EFF\",\r\n \"#518EFF\",\r\n \"#B5CFFF\",\r\n \"#B46AFF\",\r\n \"#CA97FF\",\r\n \"#E8D2FF\",\r\n \"#707070\",\r\n \"#B8B8B8\",\r\n \"#E7E7E7\",\r\n // ...docxHighlight\r\n];\r\n\r\nconst BgSelector = () => {\r\n const { editor } = useCurrentEditor();\r\n\r\n const { activeHighlightItem } = useEditorState({\r\n editor,\r\n selector: ({ editor }) => {\r\n return {\r\n activeHighlightItem: HIGHLIGHT_COLORS.find((color) =>\r\n editor.isActive(\"highlight\", { color })\r\n ),\r\n };\r\n },\r\n });\r\n\r\n return (\r\n <Popover.Root>\r\n <Popover.Trigger asChild>\r\n <Button variant=\"ghost\" className=\"h-7 gap-1 rounded px-1\">\r\n <Highlighter size={20} color={activeHighlightItem} />\r\n <ChevronDown size={12} color=\"#9fa2ab\" />\r\n </Button>\r\n </Popover.Trigger>\r\n\r\n <Popover.Content\r\n className=\"border-line shadow-box z-50 w-[242px] rounded-xl border bg-white p-4\"\r\n align=\"center\"\r\n sideOffset={18}\r\n >\r\n <div>\r\n <div\r\n className=\"text-content-first border-line mb-[10px] flex h-8 cursor-pointer items-center justify-center rounded-lg border text-sm\"\r\n onClick={() => {\r\n editor.chain().focus().unsetBackgroundColor().run();\r\n }}\r\n >\r\n 默认颜色\r\n </div>\r\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\r\n {HIGHLIGHT_COLORS.map((color) => (\r\n <span\r\n key={color}\r\n className={cn(\r\n \"hover:border-content-first flex h-7 w-7 cursor-pointer items-center justify-center rounded-sm border border-transparent\",\r\n {\r\n \"border-content-first\": activeHighlightItem === color,\r\n }\r\n )}\r\n onClick={() => {\r\n editor.chain().focus().unsetBackgroundColor().run();\r\n editor.chain().focus().setBackgroundColor(color).run();\r\n }}\r\n >\r\n <span\r\n className=\"border-line h-6 w-6 rounded-sm border\"\r\n style={{\r\n backgroundColor: color,\r\n }}\r\n />\r\n </span>\r\n ))}\r\n </div>\r\n </div>\r\n </Popover.Content>\r\n </Popover.Root>\r\n );\r\n};\r\n\r\nexport default BgSelector;\r\n","\"use client\";\r\nimport React, { useCallback } from \"react\";\r\nimport { BubbleMenu } from \"@tiptap/react/menus\";\r\nimport { NodeSelection } from \"@tiptap/pm/state\";\r\nimport { useCurrentEditor } from \"../../context\";\r\nimport { Separator } from \"../../components/ui/separator\";\r\nimport TextSelector from \"../../toolbar/components/selectors/text-selector\";\r\nimport LinkSelector from \"../../toolbar/components/selectors/link-selector\";\r\nimport ColorSelector from \"../../toolbar/components/selectors/color-selector\";\r\nimport BgSelector from \"../../toolbar/components/selectors/bg-selector\";\r\nimport { textFormatActions } from \"../../toolbarAction\";\r\nimport { isCustomNodeSelected, isTextSelected } from \"../../extensions/extension-table/utils\";\r\n\r\nconst BubbleMenuText = () => {\r\n const { editor } = useCurrentEditor();\r\n\r\n const shouldShow = useCallback(\r\n (props: any) => {\r\n // 判断富文本编辑器中是否有选中文本,如果有选中文本,则显示气泡菜单\r\n const { view, from, to } = props;\r\n if (!view || view.dragging) {\r\n return false;\r\n }\r\n\r\n const domAtPos = view.domAtPos(from || 0).node as HTMLElement;\r\n const nodeDOM = view.nodeDOM(from || 0) as HTMLElement;\r\n const node = nodeDOM || domAtPos;\r\n\r\n const isNodeSelection = editor.state.selection instanceof NodeSelection;\r\n\r\n if (isCustomNodeSelected(editor, node) || isNodeSelection) {\r\n return false;\r\n }\r\n\r\n // 检查是否选中了文本\r\n return isTextSelected({ editor });\r\n },\r\n [editor]\r\n );\r\n\r\n return (\r\n <BubbleMenu\r\n editor={editor}\r\n pluginKey=\"textBubbleMenu\"\r\n shouldShow={shouldShow}\r\n updateDelay={100}\r\n options={{\r\n placement: \"top-start\",\r\n onShow: () => {},\r\n onHide: () => {\r\n console.log(\"textBubbleMenu onHidden\");\r\n },\r\n }}\r\n appendTo={() => document.body}\r\n >\r\n <div className=\"flex h-[52px] items-center justify-center gap-3 rounded-xl border-primary/10 bg-white px-4 shadow-[0px_0px_20px_0px_rgba(0,0,0,0.1)]\">\r\n <Separator orientation=\"vertical\" className=\"bg-line h-4\" />\r\n <TextSelector textFormatActions={textFormatActions} />\r\n <LinkSelector />\r\n <ColorSelector />\r\n <BgSelector />\r\n </div>\r\n </BubbleMenu>\r\n );\r\n};\r\n\r\nexport default BubbleMenuText;\r\n","\"use client\";\r\nimport { useState, useRef } from \"react\";\r\nimport { NodeViewContent, NodeViewWrapper } from \"@tiptap/react\";\r\nimport { NodeViewProps } from \"@tiptap/core\";\r\nimport { Copy, Check, ChevronDown, ChevronRight } from \"lucide-react\";\r\nimport { Button } from \"../../components/ui/button\";\r\nimport copyTextToClipboard from \"copy-to-clipboard\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuItem,\r\n DropdownMenuTrigger,\r\n} from \"../../components/ui/dropdown-menu\";\r\nimport { ActionButton } from \"../../toolbar/components/action-button\";\r\n\r\nexport const CodeBlock = ({ language, content, extension, updateAttributes }: any) => {\r\n const [isCopied, setIsCopied] = useState(false);\r\n const [expand, setExpand] = useState(true);\r\n const contentRef = useRef<HTMLDivElement>(null);\r\n\r\n const handleCopyClick = () => {\r\n setIsCopied(true);\r\n setTimeout(() => {\r\n setIsCopied(false);\r\n }, 2000);\r\n };\r\n\r\n const CopyIcon = (props: any) => {\r\n return isCopied ? <Check color=\"#16a34a\" {...props} /> : <Copy color=\"#777\" {...props} />;\r\n };\r\n const ExpandIcon = expand ? ChevronDown : ChevronRight;\r\n const foldStyle = expand ? {} : { height: 0, overflow: \"hidden\" };\r\n\r\n return (\r\n <div className=\"overflow-hidden rounded-lg bg-muted\">\r\n <div\r\n contentEditable={false}\r\n className=\"bg-gray relative box-border flex h-9 w-full flex-row items-center justify-between px-4 py-1\"\r\n >\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n className=\"h-6 w-6 rounded-full text-content-second\"\r\n onClick={() => setExpand(!expand)}\r\n >\r\n <ExpandIcon\r\n size={14}\r\n color=\"#777\"\r\n style={{\r\n verticalAlign: \"-0.125em\",\r\n }}\r\n />\r\n </Button>\r\n\r\n <DropdownMenu>\r\n <DropdownMenuTrigger asChild>\r\n <span className=\"min-w-[50px] cursor-pointer text-sm text-[#999]\">\r\n {language || \"请选择语言\"}\r\n </span>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent className=\"max-h-96 overflow-y-auto\">\r\n {extension.options.lowlight.listLanguages().map((lang: any, index: number) => (\r\n <DropdownMenuItem\r\n key={index}\r\n onClick={() => {\r\n updateAttributes({ language: lang });\r\n }}\r\n >\r\n {lang}\r\n </DropdownMenuItem>\r\n ))}\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n\r\n <div className=\"flex gap-1\">\r\n <ActionButton\r\n onClick={() => {\r\n copyTextToClipboard(content || contentRef.current?.innerText || \"\");\r\n handleCopyClick();\r\n }}\r\n tooltip={isCopied ? \"已复制\" : \"复制\"}\r\n className=\"h-6 w-6 text-content-second\"\r\n >\r\n {/* eslint-disable-next-line react-hooks/static-components */}\r\n <CopyIcon size={16} />\r\n </ActionButton>\r\n </div>\r\n </div>\r\n <div style={foldStyle} ref={contentRef}>\r\n <pre>\r\n <NodeViewContent<\"code\"> as=\"code\" />\r\n </pre>\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\nexport const NodeViewCodeBlock = (props: NodeViewProps) => {\r\n const { node, extension, updateAttributes } = props;\r\n const language = node.attrs.language;\r\n const content = node.textContent;\r\n\r\n return (\r\n <NodeViewWrapper data-drag-handle>\r\n <CodeBlock\r\n content={content}\r\n language={language}\r\n extension={extension}\r\n updateAttributes={updateAttributes}\r\n />\r\n </NodeViewWrapper>\r\n );\r\n};\r\n","import { Editor } from \"@tiptap/core\"\r\n\r\ntype ShortcutKeyResult = {\r\n\tsymbol: string\r\n\treadable: string\r\n}\r\n\r\nexport type FileError = {\r\n\tfile: File | string\r\n\treason: \"type\" | \"size\" | \"invalidBase64\" | \"base64NotAllowed\"\r\n}\r\n\r\nexport type FileValidationOptions = {\r\n\tallowedMimeTypes: string[]\r\n\tmaxFileSize?: number\r\n\tallowBase64: boolean\r\n}\r\n\r\ntype FileInput = File | { src: string | File; alt?: string; title?: string }\r\n\r\nexport const getSelectionRawCoords = (editor: Editor) => {\r\n\tconst { view } = editor\r\n\tconst { selection } = view.state\r\n\r\n\t// 获取选区开始位置的坐标\r\n\ttry {\r\n\t\treturn view.coordsAtPos(selection.from)\r\n\t} catch (e) {\r\n\t\treturn null\r\n\t}\r\n}\r\n\r\nexport const isUrl = (\r\n\ttext: string,\r\n\toptions: { requireHostname: boolean; allowBase64?: boolean } = {\r\n\t\trequireHostname: false,\r\n\t},\r\n): boolean => {\r\n\tif (text.includes(\"\\n\")) return false\r\n\r\n\ttry {\r\n\t\tconst url = new URL(text)\r\n\t\tconst blockedProtocols = [\"javascript:\", \"file:\", \"vbscript:\", ...(options.allowBase64 ? [] : [\"data:\"])]\r\n\r\n\t\tif (blockedProtocols.includes(url.protocol)) return false\r\n\t\tif (options.allowBase64 && url.protocol === \"data:\") return /^data:image\\/[a-z]+;base64,/.test(text)\r\n\t\tif (url.hostname) return true\r\n\r\n\t\treturn url.protocol !== \"\" && (url.pathname.startsWith(\"//\") || url.pathname.startsWith(\"http\")) && !options.requireHostname\r\n\t} catch {\r\n\t\treturn false\r\n\t}\r\n}\r\n\r\nexport const sanitizeUrl = (url: string | null | undefined, options: { allowBase64?: boolean } = {}): string | undefined => {\r\n\tif (!url) return undefined\r\n\r\n\tif (options.allowBase64 && url.startsWith(\"data:image\")) {\r\n\t\treturn isUrl(url, { requireHostname: false, allowBase64: true }) ? url : undefined\r\n\t}\r\n\r\n\treturn isUrl(url, {\r\n\t\trequireHostname: false,\r\n\t\tallowBase64: options.allowBase64,\r\n\t}) || /^(\\/|#|mailto:|sms:|fax:|tel:)/.test(url)\r\n\t\t? url\r\n\t\t: `https://${url}`\r\n}\r\n\r\nexport const blobUrlToBase64 = async (blobUrl: string): Promise<string> => {\r\n\tconst response = await fetch(blobUrl)\r\n\tconst blob = await response.blob()\r\n\r\n\treturn new Promise((resolve, reject) => {\r\n\t\tconst reader = new FileReader()\r\n\t\treader.onloadend = () => {\r\n\t\t\tif (typeof reader.result === \"string\") {\r\n\t\t\t\tresolve(reader.result)\r\n\t\t\t} else {\r\n\t\t\t\treject(new Error(\"Failed to convert Blob to base64\"))\r\n\t\t\t}\r\n\t\t}\r\n\t\treader.onerror = reject\r\n\t\treader.readAsDataURL(blob)\r\n\t})\r\n}\r\n\r\nexport const randomId = (): string => Math.random().toString(36).slice(2, 11)\r\n\r\nexport const fileToBase64 = (file: File | Blob): Promise<string> => {\r\n\treturn new Promise((resolve, reject) => {\r\n\t\tconst reader = new FileReader()\r\n\t\treader.onloadend = () => {\r\n\t\t\tif (typeof reader.result === \"string\") {\r\n\t\t\t\tresolve(reader.result)\r\n\t\t\t} else {\r\n\t\t\t\treject(new Error(\"Failed to convert File to base64\"))\r\n\t\t\t}\r\n\t\t}\r\n\t\treader.onerror = reject\r\n\t\treader.readAsDataURL(file)\r\n\t})\r\n}\r\n\r\nconst validateFileOrBase64 = <T extends FileInput>(\r\n\tinput: File | string,\r\n\toptions: FileValidationOptions,\r\n\toriginalFile: T,\r\n\tvalidFiles: T[],\r\n\terrors: FileError[],\r\n): void => {\r\n\tconst { isValidType, isValidSize } = checkTypeAndSize(input, options)\r\n\r\n\tif (isValidType && isValidSize) {\r\n\t\tvalidFiles.push(originalFile)\r\n\t} else {\r\n\t\tif (!isValidType) errors.push({ file: input, reason: \"type\" })\r\n\t\tif (!isValidSize) errors.push({ file: input, reason: \"size\" })\r\n\t}\r\n}\r\n\r\nconst checkTypeAndSize = (\r\n\tinput: File | string,\r\n\t{ allowedMimeTypes, maxFileSize }: FileValidationOptions,\r\n): { isValidType: boolean; isValidSize: boolean } => {\r\n\tconst mimeType = input instanceof File ? input.type : base64MimeType(input)\r\n\tconst size = input instanceof File ? input.size : atob(input.split(\",\")[1]).length\r\n\r\n\tconst isValidType = allowedMimeTypes.length === 0 || allowedMimeTypes.includes(mimeType) || allowedMimeTypes.includes(`${mimeType.split(\"/\")[0]}/*`)\r\n\r\n\tconst isValidSize = !maxFileSize || size <= maxFileSize\r\n\r\n\treturn { isValidType, isValidSize }\r\n}\r\n\r\nconst base64MimeType = (encoded: string): string => {\r\n\tconst result = encoded.match(/data:([a-zA-Z0-9]+\\/[a-zA-Z0-9-.+]+).*,.*/)\r\n\treturn result && result.length > 1 ? result[1] : \"unknown\"\r\n}\r\n\r\nconst isBase64 = (str: string): boolean => {\r\n\tif (str.startsWith(\"data:\")) {\r\n\t\tconst matches = str.match(/^data:[^;]+;base64,(.+)$/)\r\n\t\tif (matches && matches[1]) {\r\n\t\t\tstr = matches[1]\r\n\t\t} else {\r\n\t\t\treturn false\r\n\t\t}\r\n\t}\r\n\r\n\ttry {\r\n\t\treturn btoa(atob(str)) === str\r\n\t} catch {\r\n\t\treturn false\r\n\t}\r\n}\r\n\r\nexport const filterFiles = <T extends FileInput>(files: T[], options: FileValidationOptions): [T[], FileError[]] => {\r\n\tconst validFiles: T[] = []\r\n\tconst errors: FileError[] = []\r\n\r\n\tfiles.forEach(file => {\r\n\t\tconst actualFile = \"src\" in file ? file.src : file\r\n\r\n\t\tif (actualFile instanceof File) {\r\n\t\t\tvalidateFileOrBase64(actualFile, options, file, validFiles, errors)\r\n\t\t} else if (typeof actualFile === \"string\") {\r\n\t\t\tif (isBase64(actualFile)) {\r\n\t\t\t\tif (options.allowBase64) {\r\n\t\t\t\t\tvalidateFileOrBase64(actualFile, options, file, validFiles, errors)\r\n\t\t\t\t} else {\r\n\t\t\t\t\terrors.push({ file: actualFile, reason: \"base64NotAllowed\" })\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tif (!sanitizeUrl(actualFile, { allowBase64: options.allowBase64 })) {\r\n\t\t\t\t\terrors.push({ file: actualFile, reason: \"invalidBase64\" })\r\n\t\t\t\t} else {\r\n\t\t\t\t\tvalidFiles.push(file)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t})\r\n\r\n\treturn [validFiles, errors]\r\n}\r\n\r\n// 插入到非title节点的光标位置或者文档末尾\r\nexport const getSafeInsertPosition = (state: any) => {\r\n\tif (!state) return\r\n\tconst { doc, selection } = state\r\n\r\n\tconst { from, to } = selection\r\n\r\n\tlet safeFrom = from\r\n\tlet safeTo = to\r\n\r\n\tif (from === 0 || doc.nodeAt(from)?.type.name === doc.firstChild?.type.name) {\r\n\t\tconst secondNodeStart = doc.content.child(0)?.nodeSize ? doc.content.child(0).nodeSize : doc.nodeSize\r\n\t\tsafeFrom = secondNodeStart\r\n\t\tsafeTo = secondNodeStart\r\n\t}\r\n\r\n\tconst insertPosition = safeTo || safeFrom\r\n\r\n\treturn insertPosition\r\n}\r\n","import { useState, useCallback, useEffect } from \"react\";\r\n\r\nexport type ResizeDirection = \"tl\" | \"tr\" | \"bl\" | \"br\" | \"top\" | \"right\" | \"bottom\" | \"left\";\r\nexport type ElementDimensions = { width: number; height: number };\r\n\r\ntype HookParams = {\r\n initialWidth?: number;\r\n initialHeight?: number;\r\n contentWidth?: number;\r\n contentHeight?: number;\r\n gridInterval: number;\r\n minWidth: number;\r\n minHeight: number;\r\n maxWidth: number;\r\n onDimensionsChange?: (dimensions: ElementDimensions) => void;\r\n};\r\n\r\nexport function useDragResize({\r\n initialWidth,\r\n initialHeight,\r\n contentWidth,\r\n contentHeight,\r\n gridInterval,\r\n minWidth,\r\n minHeight,\r\n maxWidth,\r\n onDimensionsChange,\r\n}: HookParams) {\r\n const [dimensions, updateDimensions] = useState<ElementDimensions>({\r\n width: Math.max(initialWidth ?? minWidth, minWidth),\r\n height: Math.max(initialHeight ?? minHeight, minHeight),\r\n });\r\n const [boundaryWidth, setBoundaryWidth] = useState(Infinity);\r\n // 修改为记录x和y坐标\r\n const [resizeOrigin, setResizeOrigin] = useState({ x: 0, y: 0 });\r\n const [initialDimensions, setInitialDimensions] = useState(dimensions);\r\n const [resizeDirection, setResizeDirection] = useState<\r\n ResizeDirection | undefined\r\n >();\r\n\r\n const widthConstraint = useCallback(\r\n (proposedWidth: number, maxAllowedWidth: number) => {\r\n const effectiveMinWidth = Math.max(\r\n minWidth,\r\n Math.min(\r\n contentWidth ?? minWidth,\r\n (gridInterval / 100) * maxAllowedWidth\r\n )\r\n );\r\n return Math.min(\r\n maxAllowedWidth,\r\n Math.max(proposedWidth, effectiveMinWidth)\r\n );\r\n },\r\n [gridInterval, contentWidth, minWidth]\r\n );\r\n\r\n const handlePointerMove = useCallback(\r\n (event: PointerEvent) => {\r\n event.preventDefault();\r\n\r\n if (!resizeDirection) return;\r\n\r\n // 计算水平和垂直方向的移动距离\r\n let deltaX = 0;\r\n let deltaY = 0;\r\n\r\n // 根据不同方向计算移动距离\r\n switch (resizeDirection) {\r\n case \"tl\": // 左上角\r\n deltaX = resizeOrigin.x - event.pageX;\r\n deltaY = resizeOrigin.y - event.pageY;\r\n break;\r\n case \"tr\": // 右上角\r\n deltaX = event.pageX - resizeOrigin.x;\r\n deltaY = resizeOrigin.y - event.pageY;\r\n break;\r\n case \"bl\": // 左下角\r\n deltaX = resizeOrigin.x - event.pageX;\r\n deltaY = event.pageY - resizeOrigin.y;\r\n break;\r\n case \"br\": // 右下角\r\n deltaX = event.pageX - resizeOrigin.x;\r\n deltaY = event.pageY - resizeOrigin.y;\r\n break;\r\n case \"top\": // 顶部 - 只改变高度\r\n deltaX = resizeOrigin.x;\r\n deltaY = resizeOrigin.y - event.pageY;\r\n break;\r\n case \"right\": // 右侧 - 只改变宽度\r\n deltaX = event.pageX - resizeOrigin.x;\r\n deltaY = resizeOrigin.y;\r\n break;\r\n case \"bottom\": // 底部 - 只改变高度\r\n deltaX = resizeOrigin.x;\r\n deltaY = event.pageY - resizeOrigin.y;\r\n break;\r\n case \"left\": // 左侧 - 只改变宽度\r\n deltaX = resizeOrigin.x - event.pageX;\r\n deltaY = resizeOrigin.y;\r\n break;\r\n }\r\n\r\n // 计算宽高比\r\n const aspectRatio =\r\n contentWidth && contentHeight\r\n ? contentWidth / contentHeight\r\n : initialDimensions.width / initialDimensions.height;\r\n\r\n let newWidth = initialDimensions.width;\r\n let newHeight = initialDimensions.height;\r\n\r\n // 根据调整方向计算新的尺寸\r\n if ([\"tl\", \"tr\", \"bl\", \"br\"].includes(resizeDirection)) {\r\n // 对角调整 - 保持宽高比\r\n const scaleFactor =\r\n Math.abs(deltaX) > Math.abs(deltaY * aspectRatio)\r\n ? deltaX / initialDimensions.width\r\n : deltaY / initialDimensions.height;\r\n\r\n newWidth = initialDimensions.width + initialDimensions.width * scaleFactor;\r\n newHeight = newWidth / aspectRatio;\r\n } else if ([\"left\", \"right\"].includes(resizeDirection)) {\r\n // 水平方向调整 - 只改变宽度\r\n newWidth = initialDimensions.width + deltaX;\r\n } else if ([\"top\", \"bottom\"].includes(resizeDirection)) {\r\n // 垂直方向调整 - 只改变高度\r\n newHeight = initialDimensions.height + deltaY;\r\n }\r\n\r\n // 应用网格对齐\r\n const gridUnitWidth = (gridInterval / 100) * boundaryWidth;\r\n \r\n if ([\"tl\", \"tr\", \"bl\", \"br\"].includes(resizeDirection)) {\r\n // 对角调整 - 保持宽高比\r\n newWidth = Math.round(newWidth / gridUnitWidth) * gridUnitWidth;\r\n newHeight = newWidth / aspectRatio;\r\n }\r\n\r\n // 应用约束\r\n if ([\"tl\", \"tr\", \"bl\", \"br\", \"left\", \"right\"].includes(resizeDirection)) {\r\n newWidth = widthConstraint(newWidth, boundaryWidth);\r\n \r\n // 只有在对角调整时才根据宽度调整高度\r\n if ([\"tl\", \"tr\", \"bl\", \"br\"].includes(resizeDirection)) {\r\n newHeight = newWidth / aspectRatio;\r\n }\r\n }\r\n\r\n updateDimensions({\r\n width: Math.max(newWidth, minWidth),\r\n height: Math.max(newHeight, minHeight),\r\n });\r\n },\r\n [\r\n widthConstraint,\r\n resizeDirection,\r\n boundaryWidth,\r\n resizeOrigin,\r\n gridInterval,\r\n contentHeight,\r\n contentWidth,\r\n initialDimensions,\r\n minWidth,\r\n minHeight,\r\n ]\r\n );\r\n\r\n const handlePointerUp = useCallback(\r\n (event: PointerEvent) => {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n\r\n setResizeOrigin({ x: 0, y: 0 });\r\n setResizeDirection(undefined);\r\n onDimensionsChange?.(dimensions);\r\n },\r\n [onDimensionsChange, dimensions]\r\n );\r\n\r\n const handleKeydown = useCallback(\r\n (event: KeyboardEvent) => {\r\n if (event.key === \"Escape\") {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n updateDimensions({\r\n width: Math.max(initialDimensions.width, minWidth),\r\n height: Math.max(initialDimensions.height, minHeight),\r\n });\r\n setResizeDirection(undefined);\r\n }\r\n },\r\n [initialDimensions, minWidth, minHeight]\r\n );\r\n\r\n const initiateResize = useCallback(\r\n (direction: ResizeDirection) =>\r\n (event: React.PointerEvent<HTMLDivElement>) => {\r\n event.preventDefault();\r\n event.stopPropagation();\r\n console.log(\"initiateResize direction\", direction);\r\n setBoundaryWidth(maxWidth);\r\n setInitialDimensions({\r\n width: Math.max(\r\n widthConstraint(dimensions.width, maxWidth),\r\n minWidth\r\n ),\r\n height: Math.max(dimensions.height, minHeight),\r\n });\r\n setResizeOrigin({ x: event.pageX, y: event.pageY });\r\n setResizeDirection(direction);\r\n },\r\n [\r\n maxWidth,\r\n widthConstraint,\r\n dimensions.width,\r\n dimensions.height,\r\n minWidth,\r\n minHeight,\r\n ]\r\n );\r\n\r\n useEffect(() => {\r\n if (resizeDirection) {\r\n document.addEventListener(\"keydown\", handleKeydown);\r\n document.addEventListener(\"pointermove\", handlePointerMove);\r\n document.addEventListener(\"pointerup\", handlePointerUp);\r\n\r\n return () => {\r\n document.removeEventListener(\"keydown\", handleKeydown);\r\n document.removeEventListener(\"pointermove\", handlePointerMove);\r\n document.removeEventListener(\"pointerup\", handlePointerUp);\r\n };\r\n }\r\n }, [resizeDirection, handleKeydown, handlePointerMove, handlePointerUp]);\r\n\r\n return {\r\n initiateResize,\r\n isResizing: !!resizeDirection,\r\n updateDimensions,\r\n currentWidth: Math.max(dimensions.width, minWidth),\r\n currentHeight: Math.max(dimensions.height, minHeight),\r\n };\r\n}\r\n","import { AlignCenter, AlignLeft, AlignRight, Maximize, Trash2 } from \"lucide-react\"\r\nimport * as React from \"react\"\r\nimport { Separator } from \"@/components/ui/separator\"\r\nimport { cn } from \"@/lib/utils\"\r\nimport { ActionButton } from \"@/toolbar/components/action-button\"\r\n\r\nexport const ActionWrapper = ({ children, className, ...props }: React.ComponentProps<\"div\">) => (\r\n\t<div\r\n\t\tclassName={cn(\r\n\t\t\t\"absolute top-3 right-3 flex flex-row rounded px-0.5 opacity-0 group-hover/node-image:opacity-100 z-[11]\",\r\n\t\t\t\"border-[0.5px] bg-[var(--mt-bg-secondary)] [backdrop-filter:saturate(1.8)_blur(20px)]\",\r\n\t\t\tclassName,\r\n\t\t)}\r\n\t\t{...props}\r\n\t>\r\n\t\t{children}\r\n\t</div>\r\n)\r\n\r\nActionWrapper.displayName = \"ActionWrapper\"\r\n\r\nexport const ImageActions: React.FC<any> = React.memo(({ editor, setImageState, updateAttributes }) => {\r\n\tconst alignButtons = [\r\n\t\t{\r\n\t\t\ticon: AlignLeft,\r\n\t\t\ttooltip: \"左对齐\",\r\n\t\t\tonClick: () => updateAttributes({ align: \"left\" }),\r\n\t\t},\r\n\t\t{\r\n\t\t\ticon: AlignCenter,\r\n\t\t\ttooltip: \"居中对齐\",\r\n\t\t\tonClick: () => updateAttributes({ align: \"center\" }),\r\n\t\t},\r\n\t\t{\r\n\t\t\ticon: AlignRight,\r\n\t\t\ttooltip: \"右对齐\",\r\n\t\t\tonClick: () => updateAttributes({ align: \"right\" }),\r\n\t\t},\r\n\t]\r\n\r\n\tconst actionButtons = [\r\n\t\t{\r\n\t\t\ticon: Maximize,\r\n\t\t\ttooltip: \"预览\",\r\n\t\t\tonClick: () => {\r\n\t\t\t\tsetImageState((prev: any) => ({ ...prev, isZoomed: true }))\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\ticon: Trash2,\r\n\t\t\ttooltip: \"移除\",\r\n\t\t\tonClick: () => {\r\n\t\t\t\teditor.commands.command(({ tr, dispatch }: any) => {\r\n\t\t\t\t\tconst { selection } = tr\r\n\t\t\t\t\tconst nodeAtSelection = tr.doc.nodeAt(selection.from)\r\n\r\n\t\t\t\t\tif (nodeAtSelection && nodeAtSelection.type.name === \"image\") {\r\n\t\t\t\t\t\tif (dispatch) {\r\n\t\t\t\t\t\t\ttr.deleteSelection()\r\n\t\t\t\t\t\t\treturn true\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn false\r\n\t\t\t\t})\r\n\t\t\t},\r\n\t\t},\r\n\t]\r\n\r\n\treturn (\r\n\t\t<ActionWrapper>\r\n\t\t\t{alignButtons.map(({ icon: Icon, tooltip, onClick }, index) => (\r\n\t\t\t\t<ActionButton key={index} tooltip={tooltip} onClick={onClick}>\r\n\t\t\t\t\t<Icon size=\"20\" />\r\n\t\t\t\t</ActionButton>\r\n\t\t\t))}\r\n\t\t\t<Separator className=\"h-5\" orientation=\"vertical\" />\r\n\t\t\t{actionButtons.map(({ icon: Icon, tooltip, onClick }, index) => (\r\n\t\t\t\t<ActionButton key={index} tooltip={tooltip} onClick={onClick}>\r\n\t\t\t\t\t<Icon size=\"20\" />\r\n\t\t\t\t</ActionButton>\r\n\t\t\t))}\r\n\t\t</ActionWrapper>\r\n\t)\r\n})\r\n\r\nImageActions.displayName = \"ImageActions\"\r\n","import * as React from 'react'\r\nimport { LoaderCircle } from \"lucide-react\"\r\nimport { cn } from '@/lib/utils'\r\n\r\nexport const ImageOverlay = React.memo(() => {\r\n return (\r\n <div\r\n className={cn(\r\n 'flex flex-row items-center justify-center',\r\n 'absolute inset-0 rounded bg-[var(--mt-overlay)] opacity-100 transition-opacity'\r\n )}\r\n >\r\n <LoaderCircle className=\"size-7 animate-spin\" />\r\n </div>\r\n )\r\n})\r\n\r\nImageOverlay.displayName = 'ImageOverlay'\r\n","import * as React from \"react\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\n// 添加四个方向\r\nconst resizeDirections = [\"tl\", \"tr\", \"bl\", \"br\"];\r\n// const resizeDirections = [\"tl\", \"tr\", \"bl\", \"br\", \"top\", \"right\", \"bottom\", \"left\"];\r\n\r\n\r\nexport const ResizeHandle = ({\r\n className,\r\n initiateResize,\r\n}: any) => {\r\n // 根据方向确定样式\r\n const directionStyles = React.useCallback((direction:any) => {\r\n const commonStyles =\r\n \"absolute z-10 block border border-primary bg-white\";\r\n\r\n let directionStyles = \"\";\r\n switch (direction) {\r\n case \"tl\":\r\n directionStyles =\r\n \"top-0 left-0 -translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-nwse-resize\";\r\n break;\r\n case \"tr\":\r\n directionStyles =\r\n \"top-0 right-0 translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-nesw-resize\";\r\n break;\r\n case \"bl\":\r\n directionStyles =\r\n \"bottom-0 left-0 -translate-x-1/2 translate-y-1/2 h-3 w-3 cursor-nesw-resize\";\r\n break;\r\n case \"br\":\r\n directionStyles =\r\n \"bottom-0 right-0 translate-x-1/2 translate-y-1/2 h-3 w-3 cursor-nwse-resize\";\r\n break;\r\n case \"top\":\r\n directionStyles =\r\n \"top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-ns-resize\";\r\n break;\r\n case \"right\":\r\n directionStyles =\r\n \"top-1/2 right-0 translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-ew-resize\";\r\n break;\r\n case \"bottom\":\r\n directionStyles =\r\n \"bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 h-3 w-3 cursor-ns-resize\";\r\n break;\r\n case \"left\":\r\n directionStyles =\r\n \"top-1/2 left-0 -translate-x-1/2 -translate-y-1/2 h-3 w-3 cursor-ew-resize\";\r\n break;\r\n default:\r\n }\r\n\r\n return cn(commonStyles, directionStyles);\r\n }, []);\r\n\r\n return (\r\n <div\r\n className={cn(\r\n \"absolute top-0 left-0 z-10 w-full h-full border border-primary cursor-pointer\",\r\n className\r\n )}\r\n >\r\n {resizeDirections?.map((direction) => {\r\n return (\r\n <span\r\n key={direction}\r\n className={directionStyles(direction)}\r\n onPointerDown={(event) => initiateResize(direction)(event)}\r\n ></span>\r\n );\r\n })}\r\n </div>\r\n );\r\n};\r\n\r\nResizeHandle.displayName = \"ResizeHandle\";\r\n","import * as React from \"react\";\r\nimport { NodeViewWrapper, type NodeViewProps } from \"@tiptap/react\";\r\nimport { Trash2, LoaderCircle } from \"lucide-react\";\r\nimport { Controlled as ControlledZoom } from \"react-medium-image-zoom\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n type ElementDimensions,\r\n useDragResize,\r\n} from \"../hooks/use-drag-resize\";\r\nimport { ActionWrapper, ImageActions } from \"./image-actions\";\r\nimport { ImageOverlay } from \"./image-overlay\";\r\nimport { ResizeHandle } from \"./resize-handle\";\r\nimport \"react-medium-image-zoom/dist/styles.css\";\r\nimport { IMAGE_MAX_HEIGHT, IMAGE_MIN_HEIGHT, IMAGE_MIN_WIDTH } from \"@/utils/constant\";\r\nimport { blobUrlToBase64 } from \"@/utils\";\r\nimport { ActionButton } from \"@/toolbar/components/action-button\";\r\n\r\n\r\ninterface ImageState {\r\n src: string;\r\n isServerUploading: boolean;\r\n imageLoaded: boolean;\r\n isZoomed: boolean;\r\n // angle: number;\r\n error: boolean;\r\n naturalSize: ElementDimensions;\r\n}\r\n\r\nexport const ImageViewBlock: React.FC<NodeViewProps> = ({\r\n editor,\r\n node,\r\n selected,\r\n updateAttributes,\r\n}) => {\r\n const {\r\n src: initialSrc,\r\n width: initialWidth,\r\n height: initialHeight,\r\n align,\r\n fileName,\r\n fileType,\r\n } = node.attrs;\r\n const [imageState, setImageState] = React.useState<ImageState>({\r\n src: initialSrc,\r\n isServerUploading: false,\r\n imageLoaded: false,\r\n isZoomed: false,\r\n // angle: 0,\r\n error: false,\r\n naturalSize: { width: initialWidth, height: initialHeight },\r\n });\r\n const containerRef = React.useRef<HTMLDivElement>(null);\r\n\r\n const onDimensionsChange = React.useCallback(\r\n ({ width, height }: ElementDimensions) => {\r\n updateAttributes({ width, height });\r\n },\r\n [updateAttributes]\r\n );\r\n\r\n const onRemoveImg = () => {\r\n editor.commands.command(({ tr, dispatch }: any) => {\r\n const { selection } = tr;\r\n const nodeAtSelection = tr.doc.nodeAt(selection.from);\r\n\r\n if (nodeAtSelection && nodeAtSelection.type.name === \"image\") {\r\n if (dispatch) {\r\n tr.deleteSelection();\r\n return true;\r\n }\r\n }\r\n return false;\r\n });\r\n };\r\n\r\n const aspectRatio =\r\n imageState.naturalSize.width / imageState.naturalSize.height;\r\n const maxWidth = IMAGE_MAX_HEIGHT * aspectRatio;\r\n const containerMaxWidth = containerRef.current\r\n ? parseFloat(\r\n getComputedStyle(containerRef.current).getPropertyValue(\r\n \"--editor-width\"\r\n )\r\n )\r\n : Infinity;\r\n\r\n const {\r\n currentWidth,\r\n currentHeight,\r\n updateDimensions,\r\n initiateResize,\r\n isResizing,\r\n } = useDragResize({\r\n initialWidth: initialWidth ?? imageState.naturalSize.width,\r\n initialHeight: initialHeight ?? imageState.naturalSize.height,\r\n contentWidth: imageState.naturalSize.width,\r\n contentHeight: imageState.naturalSize.height,\r\n gridInterval: 0.1,\r\n onDimensionsChange,\r\n minWidth: IMAGE_MIN_WIDTH,\r\n minHeight: IMAGE_MIN_HEIGHT,\r\n maxWidth: containerMaxWidth > 0 ? containerMaxWidth : maxWidth,\r\n });\r\n\r\n const handleImageLoad = React.useCallback(\r\n (ev: React.SyntheticEvent<HTMLImageElement>) => {\r\n const img = ev.target as HTMLImageElement;\r\n const newNaturalSize = {\r\n width: img.naturalWidth,\r\n height: img.naturalHeight,\r\n };\r\n setImageState((prev) => ({\r\n ...prev,\r\n naturalSize: newNaturalSize,\r\n imageLoaded: true,\r\n }));\r\n updateAttributes({\r\n width: img.width || newNaturalSize.width,\r\n height: img.height || newNaturalSize.height,\r\n alt: img.alt,\r\n title: img.title,\r\n });\r\n\r\n if (!initialWidth) {\r\n updateDimensions((state) => ({\r\n ...state,\r\n width: newNaturalSize.width,\r\n }));\r\n }\r\n },\r\n [initialWidth, updateAttributes, updateDimensions]\r\n );\r\n\r\n const handleImageError = React.useCallback(() => {\r\n setImageState((prev) => ({ ...prev, error: true, imageLoaded: true }));\r\n }, []);\r\n\r\n const handleImage = async () => {\r\n const imageExtension = editor.options.extensions.find(\r\n (ext) => ext.name === \"image\"\r\n );\r\n const { uploadFn } = imageExtension?.options ?? {};\r\n\r\n // 检查是否为blob URL或非taichu-public.wair.ac.cn域名的URL\r\n if (initialSrc.startsWith(\"blob:\") || \r\n (initialSrc.startsWith(\"http\") && initialSrc.includes(\"pixabay.com\"))) {\r\n if (imageState.isServerUploading) return;\r\n\r\n if (!uploadFn) {\r\n try {\r\n const base64 = await blobUrlToBase64(initialSrc);\r\n setImageState((prev) => ({ ...prev, src: base64 }));\r\n updateAttributes({ src: base64 });\r\n } catch {\r\n setImageState((prev) => ({ ...prev, error: true }));\r\n }\r\n } else {\r\n try {\r\n setImageState((prev) => ({ ...prev, isServerUploading: true }));\r\n\r\n let blob;\r\n if (initialSrc.startsWith(\"blob:\")) {\r\n const response = await fetch(initialSrc);\r\n blob = await response.blob();\r\n } else {\r\n // 处理非taichu-public.wair.ac.cn域名的URL\r\n const response = await fetch(initialSrc, { mode: 'cors' });\r\n blob = await response.blob();\r\n }\r\n\r\n const file = new File([blob], `${fileName || 'image'}.jpg`, {\r\n type: fileType || blob.type,\r\n });\r\n\r\n const url = await uploadFn(file);\r\n setImageState((prev) => ({\r\n ...prev,\r\n src: url,\r\n isServerUploading: false,\r\n }));\r\n updateAttributes({ src: url });\r\n } catch (error) {\r\n console.error(\"图片上传失败:\", error);\r\n setImageState((prev) => ({\r\n ...prev,\r\n error: true,\r\n isServerUploading: false,\r\n }));\r\n }\r\n }\r\n\r\n if (initialSrc.startsWith(\"blob:\")) {\r\n URL.revokeObjectURL(initialSrc);\r\n }\r\n }\r\n };\r\n\r\n React.useEffect(() => {\r\n handleImage();\r\n }, [initialSrc]);\r\n\r\n return (\r\n <NodeViewWrapper\r\n ref={containerRef}\r\n data-drag-handle\r\n className=\"relative flex justify-center\"\r\n style={{ justifyContent: align }}\r\n >\r\n <div\r\n className=\"node-image group/node-image relative rounded-md object-contain\"\r\n style={{\r\n maxWidth: `min(${maxWidth}px, 100%)`,\r\n width: currentWidth,\r\n // height: currentHeight,\r\n maxHeight: IMAGE_MAX_HEIGHT,\r\n aspectRatio: `${imageState.naturalSize.width} / ${imageState.naturalSize.height}`,\r\n }}\r\n >\r\n <div\r\n className={cn(\r\n \"relative flex h-full cursor-default flex-col items-center gap-2 rounded\"\r\n )}\r\n >\r\n <div className=\"relative h-full\">\r\n {!imageState.imageLoaded && !imageState.error && (\r\n <div className=\"absolute inset-0 flex items-center justify-center\">\r\n <LoaderCircle className=\"size-7 animate-spin\" />\r\n </div>\r\n )}\r\n\r\n <ControlledZoom\r\n isZoomed={imageState.isZoomed}\r\n onZoomChange={() =>\r\n setImageState((prev) => ({ ...prev, isZoomed: false }))\r\n }\r\n >\r\n <img\r\n className={cn(\r\n \"h-auto rounded transition-shadow\",\r\n {\r\n \"opacity-30\": !imageState.imageLoaded || imageState.error,\r\n }\r\n )}\r\n style={{\r\n // height: currentHeight,\r\n maxWidth: `min(100%, ${maxWidth}px)`,\r\n minWidth: `${IMAGE_MIN_WIDTH}px`,\r\n maxHeight: IMAGE_MAX_HEIGHT,\r\n cursor: \"pointer\",\r\n }}\r\n onDoubleClick={() => {\r\n setImageState((prev) => ({ ...prev, isZoomed: true }));\r\n }}\r\n width={currentWidth}\r\n height={currentHeight}\r\n src={imageState.src}\r\n onError={handleImageError}\r\n onLoad={handleImageLoad}\r\n alt={node.attrs.alt || \"\"}\r\n />\r\n </ControlledZoom>\r\n </div>\r\n\r\n {imageState.isServerUploading && <ImageOverlay />}\r\n {editor.isEditable &&\r\n imageState.imageLoaded &&\r\n !imageState.error &&\r\n !imageState.isServerUploading &&\r\n (selected || isResizing) && (\r\n <ResizeHandle initiateResize={initiateResize} />\r\n )}\r\n\r\n {imageState.error && (\r\n <ActionWrapper>\r\n <ActionButton tooltip=\"移除图片\" onClick={onRemoveImg}>\r\n <Trash2 className=\"size-4\" />\r\n </ActionButton>\r\n </ActionWrapper>\r\n )}\r\n\r\n {!isResizing &&\r\n !imageState.error &&\r\n !imageState.isServerUploading && (\r\n <ImageActions\r\n editor={editor}\r\n node={node}\r\n imageState={imageState}\r\n setImageState={setImageState}\r\n updateAttributes={updateAttributes}\r\n />\r\n )}\r\n </div>\r\n </div>\r\n </NodeViewWrapper>\r\n );\r\n};\r\n","import { getSafeInsertPosition } from '@/utils';\r\nimport {\r\n Image as TiptapImage,\r\n type ImageOptions,\r\n} from \"@tiptap/extension-image\";\r\nimport { ReactNodeViewRenderer, type Editor } from \"@tiptap/react\";\r\nimport { ImageViewBlock } from \"./components/image-view-block\";\r\nimport { FileError, type FileValidationOptions, filterFiles } from \"@/utils\";\r\n\r\n\r\ntype ImageAction = \"download\" | \"copyImage\" | \"copyLink\";\r\n\r\ninterface DownloadImageCommandProps {\r\n src: string;\r\n alt?: string;\r\n}\r\n\r\ninterface ImageActionProps extends DownloadImageCommandProps {\r\n action: ImageAction;\r\n}\r\n\r\ninterface CustomImageOptions\r\n extends ImageOptions,\r\n Omit<FileValidationOptions, 'allowBase64'> {\r\n uploadFn?: (file: File, editor: Editor) => Promise<string>;\r\n onActionSuccess?: (props: ImageActionProps) => void;\r\n onActionError?: (error: Error, props: ImageActionProps) => void;\r\n customDownloadImage?: (\r\n props: ImageActionProps,\r\n options: CustomImageOptions\r\n ) => Promise<void>;\r\n customCopyImage?: (\r\n props: ImageActionProps,\r\n options: CustomImageOptions\r\n ) => Promise<void>;\r\n customCopyLink?: (\r\n props: ImageActionProps,\r\n options: CustomImageOptions\r\n ) => Promise<void>;\r\n onValidationError?: (errors: FileError[]) => void;\r\n}\r\n\r\ndeclare module \"@tiptap/core\" {\r\n interface Commands<ReturnType> {\r\n setImages: {\r\n setImages: (\r\n attrs: { src: string | File; alt?: string; title?: string }[]\r\n ) => ReturnType;\r\n };\r\n setImageInline: {\r\n setImageInline: (options: any) => ReturnType;\r\n };\r\n updateImage: {\r\n updateImage: (options: any) => ReturnType;\r\n };\r\n setAlignImage: {\r\n setAlignImage: (align: string) => ReturnType;\r\n };\r\n downloadImage: {\r\n downloadImage: (attrs: DownloadImageCommandProps) => ReturnType;\r\n };\r\n copyImage: {\r\n copyImage: (attrs: DownloadImageCommandProps) => ReturnType;\r\n };\r\n copyLink: {\r\n copyLink: (attrs: DownloadImageCommandProps) => ReturnType;\r\n };\r\n }\r\n}\r\n\r\nconst handleError = (\r\n error: unknown,\r\n props: ImageActionProps,\r\n errorHandler?: (error: Error, props: ImageActionProps) => void\r\n): void => {\r\n const typedError =\r\n error instanceof Error ? error : new Error(\"Unknown error\");\r\n errorHandler?.(typedError, props);\r\n};\r\n\r\nconst defaultCopyImage = async (\r\n props: ImageActionProps,\r\n options: CustomImageOptions\r\n): Promise<void> => {\r\n const { src } = props;\r\n try {\r\n const res = await fetch(src);\r\n const blob = await res.blob();\r\n await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);\r\n options.onActionSuccess?.({ ...props, action: \"copyImage\" });\r\n } catch (error) {\r\n handleError(\r\n error,\r\n { ...props, action: \"copyImage\" },\r\n options.onActionError\r\n );\r\n }\r\n};\r\n\r\nconst defaultCopyLink = async (\r\n props: ImageActionProps,\r\n options: CustomImageOptions\r\n): Promise<void> => {\r\n const { src } = props;\r\n try {\r\n await navigator.clipboard.writeText(src);\r\n options.onActionSuccess?.({ ...props, action: \"copyLink\" });\r\n } catch (error) {\r\n handleError(error, { ...props, action: \"copyLink\" }, options.onActionError);\r\n }\r\n};\r\n\r\nconst Image = TiptapImage.extend<CustomImageOptions>({\r\n atom: true,\r\n\r\n addOptions() {\r\n return {\r\n ...this.parent?.(),\r\n allowedMimeTypes: [],\r\n maxFileSize: 0,\r\n uploadFn: undefined,\r\n };\r\n },\r\n\r\n addAttributes() {\r\n return {\r\n ...this.parent?.(),\r\n width: {\r\n default: undefined,\r\n },\r\n height: {\r\n default: undefined,\r\n },\r\n align: {\r\n default: \"center\",\r\n parseHTML: (element) => element.getAttribute(\"align\"),\r\n renderHTML: (attributes) => {\r\n return {\r\n align: attributes.align,\r\n };\r\n },\r\n },\r\n fileName: {\r\n default: undefined,\r\n },\r\n fileType: {\r\n default: undefined,\r\n },\r\n };\r\n },\r\n\r\n addCommands() {\r\n return {\r\n setImages:\r\n (attrs) =>\r\n ({ commands }) => {\r\n const [validImages, errors] = filterFiles(attrs, {\r\n allowedMimeTypes: this.options.allowedMimeTypes,\r\n maxFileSize: this.options.maxFileSize,\r\n allowBase64: this.options.allowBase64,\r\n });\r\n\r\n if (errors.length > 0 && this.options.onValidationError) {\r\n this.options.onValidationError(errors);\r\n }\r\n\r\n if (validImages.length > 0) {\r\n return commands.insertContent(\r\n validImages.map((image) => {\r\n if (image.src instanceof File) {\r\n const blobUrl = URL.createObjectURL(image.src);\r\n return {\r\n type: this.type.name,\r\n attrs: {\r\n src: blobUrl,\r\n alt: image.alt,\r\n title: image.title,\r\n fileName: image.src.name,\r\n fileType: image.src.type,\r\n width: 400,\r\n },\r\n };\r\n } else {\r\n return {\r\n type: this.type.name,\r\n attrs: {\r\n src: image.src,\r\n alt: image.alt,\r\n title: image.title,\r\n fileName: null,\r\n fileType: null,\r\n width: 400,\r\n },\r\n };\r\n }\r\n }),\r\n {\r\n updateSelection: true,\r\n insertAt: (tr:any, content:any) => {\r\n const insertPosition = getSafeInsertPosition(tr);\r\n tr.insertContentAt(content, insertPosition);\r\n },\r\n } as any\r\n );\r\n }\r\n\r\n return false;\r\n },\r\n setImageInline:\r\n (options: any) =>\r\n ({ commands }: any) => {\r\n return commands.insertContent({\r\n type: this.name,\r\n attrs: options,\r\n });\r\n },\r\n updateImage:\r\n (options) =>\r\n ({ commands }) => {\r\n console.log('updateImage options: ', options);\r\n return commands.updateAttributes(this.name, options);\r\n },\r\n setAlignImage:\r\n (align) =>\r\n ({ commands }) => {\r\n return commands.updateAttributes(this.name, { align });\r\n },\r\n copyImage: (attrs) => () => {\r\n const copyImageFunc = this.options.customCopyImage || defaultCopyImage;\r\n void copyImageFunc({ ...attrs, action: \"copyImage\" }, this.options);\r\n return true;\r\n },\r\n copyLink: (attrs) => () => {\r\n const copyLinkFunc = this.options.customCopyLink || defaultCopyLink;\r\n void copyLinkFunc({ ...attrs, action: \"copyLink\" }, this.options);\r\n return true;\r\n },\r\n };\r\n },\r\n\r\n addNodeView() {\r\n return ReactNodeViewRenderer(ImageViewBlock, {\r\n className: \"block-node\",\r\n });\r\n },\r\n});\r\n\r\nexport default Image;\r\n","import { mergeAttributes } from \"@tiptap/core\";\r\nimport TiptapLink from \"@tiptap/extension-link\";\r\nimport { getMarkRange } from \"@tiptap/core\";\r\nimport { Plugin, TextSelection } from \"@tiptap/pm/state\";\r\n\r\nexport const Link = TiptapLink.extend({\r\n inclusive: false,\r\n parseHTML() {\r\n return [\r\n {\r\n tag: 'a[href]:not([data-type=\"button\"]):not([href *= \"javascript:\" i])',\r\n },\r\n ];\r\n },\r\n renderHTML({ HTMLAttributes }) {\r\n return [\r\n \"a\",\r\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\r\n class: \"link\",\r\n }),\r\n 0,\r\n ];\r\n },\r\n addProseMirrorPlugins() {\r\n return [\r\n ...(this.parent?.() || []),\r\n new Plugin({\r\n props: {\r\n handleClick(view, pos) {\r\n const { schema, doc, tr } = view.state;\r\n const range = getMarkRange(doc.resolve(pos), schema.marks.link);\r\n\r\n if (!range) {\r\n return;\r\n }\r\n\r\n const { from, to } = range;\r\n const start = Math.min(from, to);\r\n const end = Math.max(from, to);\r\n\r\n if (pos < start || pos > end) {\r\n return;\r\n }\r\n\r\n const $start = doc.resolve(start);\r\n const $end = doc.resolve(end);\r\n const transaction = tr.setSelection(new TextSelection($start, $end));\r\n\r\n view.dispatch(transaction);\r\n },\r\n },\r\n }),\r\n ];\r\n },\r\n});\r\n\r\nexport default Link;\r\n","import { mergeAttributes, Node } from \"@tiptap/core\";\r\nimport { Plugin } from \"@tiptap/pm/state\";\r\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\r\nimport { getCellsInColumn, isRowSelected, selectRow } from \"./utils\";\r\n\r\nexport interface TableCellOptions {\r\n HTMLAttributes: Record<string, any>;\r\n}\r\n\r\nexport const TableCell = Node.create<TableCellOptions>({\r\n name: \"tableCell\",\r\n content: \"block+\",\r\n tableRole: \"cell\",\r\n isolating: true,\r\n addOptions() {\r\n return {\r\n HTMLAttributes: {},\r\n };\r\n },\r\n parseHTML() {\r\n return [{ tag: \"td\" }];\r\n },\r\n renderHTML({ HTMLAttributes }) {\r\n return [\"td\", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];\r\n },\r\n addAttributes() {\r\n return {\r\n colspan: {\r\n default: 1,\r\n parseHTML: (element) => {\r\n const colspan = element.getAttribute(\"colspan\");\r\n const value = colspan ? parseInt(colspan, 10) : 1;\r\n\r\n return value;\r\n },\r\n },\r\n rowspan: {\r\n default: 1,\r\n parseHTML: (element) => {\r\n const rowspan = element.getAttribute(\"rowspan\");\r\n const value = rowspan ? parseInt(rowspan, 10) : 1;\r\n\r\n return value;\r\n },\r\n },\r\n colwidth: {\r\n default: null,\r\n parseHTML: (element) => {\r\n const colwidth = element.getAttribute(\"colwidth\");\r\n const value = colwidth ? colwidth.split(\",\").map((width) => parseInt(width, 10)) : null;\r\n\r\n return value;\r\n },\r\n },\r\n style: {\r\n default: null,\r\n },\r\n };\r\n },\r\n addProseMirrorPlugins() {\r\n const { isEditable } = this.editor;\r\n\r\n return [\r\n new Plugin({\r\n props: {\r\n decorations: (state) => {\r\n if (!isEditable) {\r\n return DecorationSet.empty;\r\n }\r\n\r\n const { doc, selection } = state;\r\n const decorations: Decoration[] = [];\r\n const cells = getCellsInColumn(0)(selection);\r\n\r\n if (cells) {\r\n cells.forEach(({ pos }: { pos: number }, index: number) => {\r\n decorations.push(\r\n Decoration.widget(pos + 1, () => {\r\n const rowSelected = isRowSelected(index)(selection);\r\n let className = \"grip-row\";\r\n\r\n if (rowSelected) {\r\n className += \" selected\";\r\n }\r\n\r\n if (index === 0) {\r\n className += \" first\";\r\n }\r\n\r\n if (index === cells.length - 1) {\r\n className += \" last\";\r\n }\r\n\r\n const grip = document.createElement(\"a\");\r\n\r\n grip.className = className;\r\n grip.addEventListener(\"mousedown\", (event) => {\r\n event.preventDefault();\r\n event.stopImmediatePropagation();\r\n\r\n this.editor.view.dispatch(selectRow(index)(this.editor.state.tr));\r\n });\r\n\r\n return grip;\r\n })\r\n );\r\n });\r\n }\r\n\r\n return DecorationSet.create(doc, decorations);\r\n },\r\n },\r\n }),\r\n ];\r\n },\r\n});\r\n","import { TableHeader as TiptapTableHeader } from \"@tiptap/extension-table\";\r\nimport { Plugin } from \"@tiptap/pm/state\";\r\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\r\n\r\nimport { getCellsInRow, isColumnSelected, selectColumn } from \"./utils\";\r\n\r\nexport { type TableHeaderOptions } from \"@tiptap/extension-table\";\r\n\r\nexport const TableHeader = TiptapTableHeader.extend({\r\n addProseMirrorPlugins() {\r\n const { isEditable } = this.editor;\r\n\r\n return [\r\n new Plugin({\r\n props: {\r\n decorations: (state) => {\r\n if (!isEditable) {\r\n return DecorationSet.empty;\r\n }\r\n\r\n const { doc, selection } = state;\r\n const decorations: Decoration[] = [];\r\n const cells = getCellsInRow(0)(selection);\r\n\r\n if (cells) {\r\n cells.forEach(({ pos }: { pos: number }, index: number) => {\r\n decorations.push(\r\n Decoration.widget(pos + 1, () => {\r\n const colSelected = isColumnSelected(index)(selection);\r\n let className = \"grip-column\";\r\n\r\n if (colSelected) {\r\n className += \" selected\";\r\n }\r\n\r\n if (index === 0) {\r\n className += \" first\";\r\n }\r\n\r\n if (index === cells.length - 1) {\r\n className += \" last\";\r\n }\r\n\r\n const grip = document.createElement(\"a\");\r\n\r\n grip.className = className;\r\n grip.addEventListener(\"mousedown\", (event) => {\r\n event.preventDefault();\r\n event.stopImmediatePropagation();\r\n\r\n this.editor.view.dispatch(selectColumn(index)(this.editor.state.tr));\r\n });\r\n\r\n return grip;\r\n })\r\n );\r\n });\r\n }\r\n\r\n return DecorationSet.create(doc, decorations);\r\n },\r\n },\r\n }),\r\n ];\r\n },\r\n});\r\n\r\nexport default TableHeader;\r\n","import { TableRow as TiptapTableRow } from \"@tiptap/extension-table\";\r\nexport { type TableRowOptions } from \"@tiptap/extension-table\";\r\n\r\nexport const TableRow = TiptapTableRow.extend({\r\n allowGapCursor: false,\r\n // content: 'tableCell*',\r\n});\r\n\r\nexport default TableRow;\r\n","import { Table as TipTable } from \"@tiptap/extension-table\";\r\n// import { TableCell, type TableCellOptions } from '@tiptap/extension-table-cell'\r\nimport { TableCell, type TableCellOptions } from \"./cell\";\r\n// import { TableHeader, type TableHeaderOptions } from '@tiptap/extension-table-header'\r\nimport { TableHeader, type TableHeaderOptions } from \"./header\";\r\n// import { TableRow, type TableRowOptions } from '@tiptap/extension-table-row'\r\nimport { TableRow, type TableRowOptions } from \"./row\";\r\n// import { type TableCellBackgroundOptions, TableCellBackground } from './widget/cell-background'\r\n\r\nexport interface TableOptions {\r\n HTMLAttributes: Record<string, any>;\r\n resizable: boolean;\r\n handleWidth?: number;\r\n cellMinWidth?: number;\r\n lastColumnResizable: boolean;\r\n allowTableNodeSelection: boolean;\r\n tableRow?: Partial<TableRowOptions>;\r\n tableHeader?: Partial<TableHeaderOptions>;\r\n tableCell?: Partial<TableCellOptions>;\r\n // tableCellBackground: Partial<TableCellBackgroundOptions>\r\n}\r\n\r\nexport const Table = TipTable.extend<TableOptions>({\r\n addOptions() {\r\n return {\r\n ...this.parent?.(),\r\n resizable: true,\r\n lastColumnResizable: true,\r\n allowTableNodeSelection: true,\r\n HTMLAttributes: {},\r\n };\r\n },\r\n\r\n addExtensions() {\r\n return [\r\n TableRow.configure(this.options.tableRow),\r\n TableHeader.configure(this.options.tableHeader),\r\n TableCell.configure(this.options.tableCell),\r\n ];\r\n },\r\n});\r\n\r\nexport default Table;\r\n","import CodeBlockLowlight from \"@tiptap/extension-code-block-lowlight\"\nimport FileHandler from \"@tiptap/extension-file-handler\"\nimport { TaskItem, TaskList } from \"@tiptap/extension-list\"\nimport TextAlign from \"@tiptap/extension-text-align\"\nimport { BackgroundColor, Color, TextStyleKit } from \"@tiptap/extension-text-style\"\nimport { Dropcursor, Focus, Gapcursor, Placeholder, Selection } from \"@tiptap/extensions\"\nimport { type Editor, type Extensions, ReactNodeViewRenderer } from \"@tiptap/react\"\nimport StarterKit from \"@tiptap/starter-kit\"\nimport css from \"highlight.js/lib/languages/css\"\nimport js from \"highlight.js/lib/languages/javascript\"\nimport ts from \"highlight.js/lib/languages/typescript\"\nimport html from \"highlight.js/lib/languages/xml\"\nimport { common, createLowlight } from \"lowlight\"\nimport { NodeViewCodeBlock } from \"./extension-code-block/code-block\"\nimport Image from \"./extension-image\"\nimport \"highlight.js/styles/github.css\"\nimport { Mathematics } from \"@tiptap/extension-mathematics\"\nimport { toast } from \"sonner\"\nimport { IMAGE_MAX_SIZE } from \"../utils/constant\"\nimport Link from \"./extension-link\"\nimport Table from \"./extension-table\"\n\nconst lowlight = createLowlight(common)\n\n// you can also register individual languages\nlowlight.register(\"html\", html)\nlowlight.register(\"css\", css)\nlowlight.register(\"js\", js)\nlowlight.register(\"ts\", ts)\n\nexport interface GetExtensionsProps {\n\t// 上传文件\n\tuploadFn?: (file: File, editor: Editor) => Promise<string>\n}\n\nconst getExtensions = (props: GetExtensionsProps) => {\n\tconst { uploadFn } = props\n\n\tconst extensions: Extensions = [\n\t\tTextStyleKit,\n\t\tStarterKit,\n\t\tPlaceholder.configure({\n\t\t\tplaceholder: ({ node }) => {\n\t\t\t\tconst nodeTypeName = node?.type?.name\n\t\t\t\tif (nodeTypeName === \"title\") {\n\t\t\t\t\treturn \"未命名文档\"\n\t\t\t\t}\n\t\t\t\tif (nodeTypeName === \"heading\") {\n\t\t\t\t\treturn `标题${node.attrs.level}`\n\t\t\t\t}\n\t\t\t\tif (nodeTypeName === \"codeBlock\") {\n\t\t\t\t\treturn \"请输入代码\"\n\t\t\t\t}\n\t\t\t\tif (nodeTypeName === \"table\") {\n\t\t\t\t\treturn \"\"\n\t\t\t\t}\n\t\t\t\treturn `请输入内容`\n\t\t\t},\n\t\t}),\n\t\tFocus.configure({\n\t\t\tclassName: \"node-focused\",\n\t\t\tmode: \"all\",\n\t\t}),\n\t\tTextAlign.configure({\n\t\t\ttypes: [\"heading\", \"paragraph\"],\n\t\t}),\n\t\tColor,\n\t\tBackgroundColor,\n\t\tTaskList,\n\t\tTaskItem,\n\t\tTable,\n\t\tGapcursor,\n\t\tImage.configure({\n\t\t\tallowedMimeTypes: [\"image/*\"],\n\t\t\tmaxFileSize: IMAGE_MAX_SIZE * 1024 * 1024,\n\t\t\tallowBase64: false,\n\t\t\tuploadFn: uploadFn,\n\t\t\tonValidationError(errors) {\n\t\t\t\terrors.forEach(error => {\n\t\t\t\t\ttoast.error(\"图片校验失败\", {\n\t\t\t\t\t\tdescription: error.reason,\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t},\n\t\t\tonActionSuccess({ action }) {\n\t\t\t\tconst mapping = {\n\t\t\t\t\tcopyImage: \"复制图片\",\n\t\t\t\t\tcopyLink: \"复制链接\",\n\t\t\t\t\tdownload: \"下载\",\n\t\t\t\t}\n\t\t\t\ttoast.success(`${mapping[action]}成功`)\n\t\t\t},\n\t\t\tonActionError(error, { action }) {\n\t\t\t\tconst mapping = {\n\t\t\t\t\tcopyImage: \"复制图片\",\n\t\t\t\t\tcopyLink: \"复制链接\",\n\t\t\t\t\tdownload: \"下载\",\n\t\t\t\t}\n\t\t\t\ttoast.error(`${mapping[action]}失败`, {\n\t\t\t\t\tdescription: error.message,\n\t\t\t\t})\n\t\t\t},\n\t\t}),\n\t\tDropcursor,\n\t\tFileHandler.configure({\n\t\t\tallowedMimeTypes: [\"image/png\", \"image/jpeg\", \"image/gif\", \"image/webp\"],\n\t\t\tonDrop: (currentEditor, files, pos) => {\n\t\t\t\tfiles.forEach(async file => {\n\t\t\t\t\tconst localSrc = URL.createObjectURL(file)\n\n\t\t\t\t\tcurrentEditor\n\t\t\t\t\t\t.chain()\n\t\t\t\t\t\t.insertContentAt(pos, {\n\t\t\t\t\t\t\ttype: \"image\",\n\t\t\t\t\t\t\tattrs: {\n\t\t\t\t\t\t\t\tsrc: localSrc,\n\t\t\t\t\t\t\t\tfileName: file.name,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.focus()\n\t\t\t\t\t\t.run()\n\n\t\t\t\t\tif (uploadFn) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst remoteSrc = await uploadFn(file, currentEditor)\n\t\t\t\t\t\t\tcurrentEditor.commands.updateAttributes(\"image\", { src: remoteSrc })\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error(\"上传失败:\", error)\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tURL.revokeObjectURL(localSrc)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t\tonPaste: (currentEditor, files, htmlContent) => {\n\t\t\t\tif (htmlContent) return false\n\n\t\t\t\tfiles.forEach(async file => {\n\t\t\t\t\tconst localSrc = URL.createObjectURL(file)\n\n\t\t\t\t\tcurrentEditor\n\t\t\t\t\t\t.chain()\n\t\t\t\t\t\t.insertContentAt(currentEditor.state.selection.anchor, {\n\t\t\t\t\t\t\ttype: \"image\",\n\t\t\t\t\t\t\tattrs: { src: localSrc },\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.focus()\n\t\t\t\t\t\t.run()\n\n\t\t\t\t\tif (uploadFn) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst remoteSrc = await uploadFn(file, currentEditor)\n\t\t\t\t\t\t\tcurrentEditor.commands.updateAttributes(\"image\", { src: remoteSrc })\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconsole.error(\"粘贴上传失败:\", error)\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tURL.revokeObjectURL(localSrc)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t}),\n\t\tSelection.configure({\n\t\t\tclassName: \"selection\",\n\t\t}),\n\t\tLink.configure({\n\t\t\topenOnClick: \"whenNotEditable\",\n\t\t}),\n\t\tCodeBlockLowlight.extend({\n\t\t\taddNodeView() {\n\t\t\t\treturn ReactNodeViewRenderer(NodeViewCodeBlock)\n\t\t\t},\n\t\t}).configure({\n\t\t\tlowlight: lowlight,\n\t\t}),\n\t\tMathematics,\n\t]\n\n\treturn extensions\n}\n\nexport { getExtensions }\n","import { memo } from \"react\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\n\r\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../../components/ui/popover\";\r\nimport { Button } from \"../../../components/ui/button\";\r\nimport { useCurrentEditor } from \"../../../context\";\r\nimport { cn } from \"../../../lib/utils\";\r\nimport { Check, ChevronDown } from \"lucide-react\";\r\nimport { IActionItem, paragraph } from \"../../../toolbarAction\";\r\nimport { useEditorDerivedState } from \"../../../hooks/useEditorDerivedState\";\r\n\r\ninterface IProps {\r\n nodeFormatActions: IActionItem[];\r\n}\r\n\r\nconst NodeSelector = ({ nodeFormatActions }: IProps) => {\r\n const { editor } = useCurrentEditor();\r\n const state = useEditorDerivedState();\r\n const activeItem =\r\n nodeFormatActions.find((item) => {\r\n return item.isActive?.(state);\r\n }) ?? paragraph;\r\n\r\n return (\r\n <Popover>\r\n <PopoverTrigger asChild>\r\n <Button variant=\"ghost\" className=\"h-7 gap-1 rounded px-1\">\r\n <activeItem.icon size={20} />\r\n <ChevronDown size={12} color=\"#9fa2ab\" />\r\n </Button>\r\n </PopoverTrigger>\r\n <PopoverContent\r\n className=\"border-line z-50 w-40 rounded-xl border bg-white px-2 py-1 shadow-xl\"\r\n align=\"start\"\r\n sideOffset={18}\r\n >\r\n {nodeFormatActions.map((item) => {\r\n return (\r\n <div\r\n key={item.id}\r\n onClick={() => {\r\n item.onClick(editor);\r\n }}\r\n className={cn(\r\n \"flex cursor-pointer items-center rounded-sm p-2 text-sm hover:bg-accent\",\r\n activeItem.label === item.label ? \"text-primary\" : \"\"\r\n )}\r\n >\r\n <item.icon size=\"20\" className=\"mr-3\" />\r\n <span>{item.label}</span>\r\n {activeItem.label === item.label && <Check size=\"16\" className=\"ml-auto items-end\" />}\r\n </div>\r\n );\r\n })}\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n};\r\n\r\nexport default memo(NodeSelector);\r\n","\"use client\";\r\nimport { memo, useState } from \"react\";\r\n\r\nimport { Button } from \"../../../components/ui/button\";\r\nimport { useCurrentEditor } from \"../../../context\";\r\nimport { Check, ChevronDown } from \"lucide-react\";\r\nimport { cn } from \"../../../lib/utils\";\r\nimport { useEditorDerivedState } from \"../../../hooks/useEditorDerivedState\";\r\nimport { Popover, PopoverTrigger, PopoverContent } from \"../../../components/ui/popover\";\r\nimport { leftAlign, textAlignActions } from \"../../../toolbarAction\";\r\n\r\nconst AlignSelector = () => {\r\n const { editor } = useCurrentEditor();\r\n\r\n const [open, setOpen] = useState(false);\r\n\r\n const state = useEditorDerivedState();\r\n\r\n const activeItem = textAlignActions.find((item) => item.isActive?.(state)) ?? leftAlign;\r\n\r\n return (\r\n <Popover open={open} onOpenChange={setOpen}>\r\n <PopoverTrigger asChild>\r\n <Button onClick={() => setOpen(true)} variant=\"ghost\" className=\"h-7 gap-1 rounded px-1\">\r\n <activeItem.icon strokeWidth={1.5} size=\"20\" />\r\n <ChevronDown size={12} color=\"#9fa2ab\" />\r\n </Button>\r\n </PopoverTrigger>\r\n\r\n <PopoverContent\r\n className=\"border-line z-50 w-40 rounded-xl border bg-white px-2 py-1 shadow-xl\"\r\n align=\"start\"\r\n sideOffset={18}\r\n >\r\n {textAlignActions.map((item) => {\r\n return (\r\n <div\r\n key={item.id}\r\n onClick={() => {\r\n item.onClick(editor);\r\n setOpen(false);\r\n }}\r\n className={cn(\r\n \"flex cursor-pointer items-center rounded-sm p-2 text-sm hover:bg-accent\",\r\n activeItem?.label === item.label ? \"text-primary\" : \"\"\r\n )}\r\n >\r\n <item.icon size=\"20\" className=\"mr-3\" />\r\n <span>{item.label}</span>\r\n {activeItem?.label === item.label && (\r\n <Check size=\"16\" className=\"ml-auto items-end\" />\r\n )}\r\n </div>\r\n );\r\n })}\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n};\r\n\r\nexport default memo(AlignSelector);\r\n","import { ImagePlus } from \"lucide-react\";\r\nimport { useCurrentEditor } from \"../../../context\";\r\nimport { Button } from \"../../../components/ui/button\";\r\n\r\nconst InsetImageButton = () => {\r\n const { editor } = useCurrentEditor();\r\n\r\n return (\r\n <Button\r\n variant=\"ghost\"\r\n className=\"aspect-square h-7 gap-1 rounded px-1\"\r\n onClick={() => {\r\n const input = document.createElement(\"input\");\r\n input.type = \"file\";\r\n input.accept = \"image/*\";\r\n input.onchange = async (event) => {\r\n const file = (event.target as HTMLInputElement).files?.[0];\r\n if (file) {\r\n editor.commands.setImages([{ src: file }]);\r\n }\r\n };\r\n input.click();\r\n }}\r\n >\r\n <ImagePlus size=\"20\" />\r\n </Button>\r\n );\r\n};\r\n\r\nexport default InsetImageButton;\r\n","import React from \"react\";\r\nimport { ToolbarButton } from \"../action-button\";\r\nimport { Table } from \"lucide-react\";\r\nimport { useCurrentEditor } from \"../../../context\";\r\nimport { useEditorDerivedState } from \"../../../hooks/useEditorDerivedState\";\r\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../../components/ui/popover\";\r\nimport { TABLE_DEFAULT_SELECTED_GRID_SIZE, TABLE_INIT_GRID_SIZE } from \"../../../utils/constant\";\r\n\r\nconst createArray = (length: number) => Array.from({ length }).map((_, index) => index + 1);\r\n\r\ninterface IPropsCreateTablePopover {\r\n createTable: any;\r\n children: any;\r\n}\r\n\r\nexport interface GridSize {\r\n rows: number;\r\n cols: number;\r\n}\r\n\r\nexport interface CreateTablePayload extends GridSize {\r\n withHeaderRow: boolean;\r\n}\r\n\r\nfunction TableActionSelector() {\r\n const { editor } = useCurrentEditor();\r\n const { canTable, isTable } = useEditorDerivedState();\r\n\r\n function createTable(options: any) {\r\n if (canTable) {\r\n editor\r\n .chain()\r\n .focus()\r\n .insertTable({ ...options, withHeaderRow: false })\r\n .run();\r\n }\r\n }\r\n\r\n const [withHeaderRow, setWithHeaderRow] = React.useState<boolean>(true);\r\n const [tableGridSize, setTableGridSize] = React.useState<GridSize>({\r\n rows: TABLE_INIT_GRID_SIZE,\r\n cols: TABLE_INIT_GRID_SIZE,\r\n });\r\n\r\n const [selectedTableGridSize, setSelectedTableGridSize] = React.useState<GridSize>({\r\n rows: TABLE_DEFAULT_SELECTED_GRID_SIZE,\r\n cols: TABLE_DEFAULT_SELECTED_GRID_SIZE,\r\n });\r\n\r\n function selectTableGridSize(rows: number, cols: number): void {\r\n if (rows === tableGridSize.rows) {\r\n setTableGridSize((prev) => {\r\n return {\r\n ...prev,\r\n rows: Math.min(rows + 1, TABLE_INIT_GRID_SIZE),\r\n };\r\n });\r\n }\r\n\r\n if (cols === tableGridSize.cols) {\r\n setTableGridSize((prev) => {\r\n return {\r\n ...prev,\r\n cols: Math.min(cols + 1, TABLE_INIT_GRID_SIZE),\r\n };\r\n });\r\n }\r\n\r\n setSelectedTableGridSize({\r\n rows,\r\n cols,\r\n });\r\n }\r\n\r\n function onMouseDown(rows: number, cols: number) {\r\n createTable({ rows, cols, withHeaderRow });\r\n resetTableGridSize();\r\n }\r\n\r\n function resetTableGridSize(): void {\r\n setWithHeaderRow(false);\r\n\r\n setTableGridSize({\r\n rows: TABLE_INIT_GRID_SIZE,\r\n cols: TABLE_INIT_GRID_SIZE,\r\n });\r\n\r\n setSelectedTableGridSize({\r\n rows: TABLE_DEFAULT_SELECTED_GRID_SIZE,\r\n cols: TABLE_DEFAULT_SELECTED_GRID_SIZE,\r\n });\r\n }\r\n\r\n return (\r\n <Popover>\r\n <PopoverTrigger asChild>\r\n <ToolbarButton\r\n data-state=\"off\"\r\n disabled={!canTable || isTable}\r\n tooltip=\"插入表格\"\r\n tooltipOptions={{ side: \"bottom\" }}\r\n isActive={isTable}\r\n >\r\n <Table size={20} />\r\n </ToolbarButton>\r\n </PopoverTrigger>\r\n <PopoverContent className=\"z-50 w-full !p-2\" align=\"start\" side=\"bottom\" sideOffset={18}>\r\n <div className=\"table-grid-size-editor p-0\">\r\n <div className=\"flex flex-col flex-wrap justify-between gap-1\">\r\n {createArray(tableGridSize?.rows)?.map((row: any) => {\r\n return (\r\n <div key={`r-${row}`} className=\"flex gap-1\">\r\n {createArray(tableGridSize?.cols)?.map((col: any) => {\r\n return (\r\n <div\r\n key={`c-${col}`}\r\n className={`border-line box-border h-4 w-4 cursor-pointer rounded-[2px] border-solid bg-gray-200 ${\r\n col <= selectedTableGridSize.cols && row <= selectedTableGridSize.rows\r\n ? \"bg-primary/60\"\r\n : \"bg-gray-100\"\r\n }`}\r\n onMouseOver={() => selectTableGridSize(row, col)}\r\n onMouseDown={() => onMouseDown(row, col)}\r\n ></div>\r\n );\r\n })}\r\n </div>\r\n );\r\n })}\r\n </div>\r\n <div className=\"mt-2 text-center text-sm text-zinc-600\">\r\n {selectedTableGridSize.rows} x {selectedTableGridSize.cols}\r\n </div>\r\n </div>\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n}\r\n\r\nexport default TableActionSelector;\r\n","import { useCurrentEditor } from \"../context\";\r\nimport { IActionItem, redo, textFormatActions, toolbarNodeConfig, undo } from \"../toolbarAction\";\r\nimport { ToolbarButton } from \"./components/action-button\";\r\nimport NodeSelector from \"./components/selectors/node-selector\";\r\nimport TextSelector from \"./components/selectors/text-selector\";\r\nimport LinkSelector from \"./components/selectors/link-selector\";\r\nimport ColorSelector from \"./components/selectors/color-selector\";\r\nimport BgSelector from \"./components/selectors/bg-selector\";\r\nimport AlignSelector from \"./components/selectors/align-selector\";\r\nimport { useEditorDerivedState } from \"../hooks/useEditorDerivedState\";\r\nimport InsetImageSelector from \"./components/selectors/inset-image-selector\";\r\nimport TableActionSelector from \"./components/selectors/table-action-selector\";\r\nimport { Separator } from \"../components/ui/separator\";\r\nimport { type Editor } from \"@tiptap/core\";\r\nimport React from \"react\";\r\n\r\nexport type ToolbarItemKey =\r\n | \"undo\"\r\n | \"redo\"\r\n | \"node-selector\"\r\n | \"text-selector\"\r\n | \"link\"\r\n | \"color\"\r\n | \"bg\"\r\n | \"align\"\r\n | \"image\"\r\n | \"table\"\r\n | \"separator\";\r\n\r\nexport interface ToolbarConfigItem {\r\n key: ToolbarItemKey | string; // 支持内置 key 或自定义 key\r\n render?: (editor: Editor) => React.ReactNode; // 如果是自定义组件,通过 render 函数传入\r\n}\r\n\r\nexport interface ToolbarProps {\r\n config?: (ToolbarItemKey | ToolbarConfigItem)[]; // 接收 key 数组或对象数组\r\n appendItems?: React.ReactNode; // 方便在末尾快速追加\r\n}\r\n\r\nconst DEFAULT_CONFIG: ToolbarItemKey[] = [\r\n \"undo\",\r\n \"redo\",\r\n \"separator\",\r\n \"node-selector\",\r\n \"text-selector\",\r\n \"link\",\r\n \"color\",\r\n \"bg\",\r\n \"align\",\r\n \"separator\",\r\n \"image\",\r\n \"table\",\r\n];\r\n\r\nconst Toolbar = ({ config = DEFAULT_CONFIG, appendItems }: ToolbarProps) => {\r\n const { editor } = useCurrentEditor();\r\n const state = useEditorDerivedState();\r\n\r\n if (!editor) return null;\r\n\r\n const getBtnProps = (item: any) => ({\r\n isActive: item.isActive?.(state) ?? false,\r\n disabled: item.disabled?.(state) ?? false,\r\n onClick: () => item.onClick(editor),\r\n });\r\n\r\n const renderItem = (item: ToolbarItemKey | ToolbarConfigItem, index: number) => {\r\n // 处理字符串类型的配置\r\n const key = typeof item === \"string\" ? item : item.key;\r\n\r\n switch (key) {\r\n case \"undo\":\r\n return (\r\n <ToolbarButton\r\n key={index}\r\n {...getBtnProps(undo)}\r\n tooltip={undo.label}\r\n shortcutKeys={undo.shortcutKeys}\r\n >\r\n <undo.icon size={20} />\r\n </ToolbarButton>\r\n );\r\n case \"redo\":\r\n return (\r\n <ToolbarButton\r\n key={index}\r\n {...getBtnProps(redo)}\r\n tooltip={redo.label}\r\n shortcutKeys={redo.shortcutKeys}\r\n >\r\n <redo.icon size={20} />\r\n </ToolbarButton>\r\n );\r\n case \"separator\":\r\n return <Separator key={index} orientation=\"vertical\" className=\"bg-line h-4\" />;\r\n case \"node-selector\":\r\n return <NodeSelector key={index} nodeFormatActions={toolbarNodeConfig.select} />;\r\n case \"text-selector\":\r\n return <TextSelector key={index} textFormatActions={textFormatActions} />;\r\n case \"link\":\r\n return <LinkSelector key={index} />;\r\n case \"color\":\r\n return <ColorSelector key={index} />;\r\n case \"bg\":\r\n return <BgSelector key={index} />;\r\n case \"align\":\r\n return <AlignSelector key={index} />;\r\n case \"image\":\r\n return <InsetImageSelector key={index} />;\r\n case \"table\":\r\n return <TableActionSelector key={index} />;\r\n default:\r\n // 如果是自定义配置且带有 render 函数\r\n if (typeof item !== \"string\" && item.render) {\r\n return <React.Fragment key={index}>{item.render(editor)}</React.Fragment>;\r\n }\r\n return null;\r\n }\r\n };\r\n\r\n return (\r\n <div className=\"z-99 flex flex-wrap items-center gap-1 border-b bg-white px-4 py-2 shadow-sm\">\r\n {config.map((item, idx) => renderItem(item, idx))}\r\n {appendItems}\r\n </div>\r\n );\r\n};\r\n\r\nexport { Toolbar };\r\n","\"use client\"\r\nimport { type Editor } from \"@tiptap/core\"\r\nimport { EditorContent, useEditor } from \"@tiptap/react\"\r\nimport { Toaster } from \"@/components/ui/sonner\"\r\nimport BubbleMenuBlock from \"../bubble-menu/bubble-menu-block\"\r\nimport BubbleMenuTable from \"../bubble-menu/bubble-menu-table\"\r\nimport BubbleMenuText from \"../bubble-menu/bubble-menu-text\"\r\nimport { EditorProvider } from \"../context\"\r\nimport { getExtensions } from \"../extensions\"\r\nimport { cn } from \"../lib/utils\"\r\nimport { Toolbar, type ToolbarProps } from \"../toolbar/index\"\r\n\r\nexport interface TiptapComponentsProps {\r\n\tvalue: string\r\n\tonChange: (value: string) => void\r\n\tonImageUpload?: () => Promise<string>\r\n\tclassName?: string\r\n\tshowToolbar?: boolean\r\n\tshowBubbleMenu?: boolean\r\n\tshowTableMenu?: boolean\r\n\tshowBlockMenu?: boolean\r\n\ttoolbarProps?: ToolbarProps\r\n\tonInitEditor?: (editor: Editor) => void\r\n}\r\n\r\nexport const TiptapComponent = ({\r\n\tvalue,\r\n\tonChange,\r\n\tonImageUpload,\r\n\tclassName,\r\n\tshowToolbar = true,\r\n\tshowBubbleMenu = true,\r\n\tshowTableMenu = true,\r\n\tshowBlockMenu = true,\r\n\ttoolbarProps,\r\n\tonInitEditor,\r\n}: TiptapComponentsProps) => {\r\n\tconst editor = useEditor({\r\n\t\timmediatelyRender: false,\r\n\t\textensions: getExtensions({ uploadFn: onImageUpload }),\r\n\t\tcontent: value,\r\n\t\teditorProps: {\r\n\t\t\tattributes: {\r\n\t\t\t\tclass: \"w-full focus:outline-none\",\r\n\t\t\t},\r\n\t\t},\r\n\t\tonUpdate: ({ editor }) => {\r\n\t\t\tonChange(editor.getHTML())\r\n\t\t},\r\n\t\tonCreate: ({ editor }) => {\r\n\t\t\tonInitEditor?.(editor)\r\n\t\t},\r\n\t})\r\n\r\n\tif (!editor) {\r\n\t\treturn null\r\n\t}\r\n\r\n\treturn (\r\n\t\t<EditorProvider editor={editor}>\r\n\t\t\t{showToolbar && <Toolbar {...toolbarProps} />}\r\n\t\t\t{showBubbleMenu && <BubbleMenuText />}\r\n\t\t\t{showTableMenu && <BubbleMenuTable />}\r\n\t\t\t{showBlockMenu && <BubbleMenuBlock />}\r\n\t\t\t<EditorContent editor={editor} className={cn(\"w-full not-prose\",!showToolbar && 'not-toolbar', className)} />\r\n\t\t\t<Toaster richColors position=\"top-center\" />\r\n\t\t</EditorProvider>\r\n\t)\r\n}\r\n"],"x_google_ignoreList":[0,2,3,4],"mappings":"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"}