@wair/editor 0.0.1-beta.1

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.
@@ -0,0 +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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"}