tiptap-notion-editor 0.5.0
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.
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/lib/NotionEditor.d.ts +58 -0
- package/lib/components/Draggable/index.d.ts +5 -0
- package/lib/components/ToggleButton.d.ts +16 -0
- package/lib/components/TooltipWrapper.d.ts +9 -0
- package/lib/components/blocks/attachment.d.ts +2 -0
- package/lib/components/blocks/code.d.ts +2 -0
- package/lib/components/blocks/emoji.d.ts +2 -0
- package/lib/components/blocks/image.d.ts +2 -0
- package/lib/components/blocks/list.d.ts +4 -0
- package/lib/components/blocks/separator.d.ts +2 -0
- package/lib/components/blocks/table.d.ts +2 -0
- package/lib/components/blocks/tableOfContents.d.ts +2 -0
- package/lib/components/blocks/types.d.ts +26 -0
- package/lib/components/blocks/typography.d.ts +8 -0
- package/lib/components/bubble/BubbleMoreOption.d.ts +5 -0
- package/lib/components/bubble/ImageBubbleMenu.d.ts +7 -0
- package/lib/components/bubble/IndentButtons.d.ts +5 -0
- package/lib/components/bubble/MyBubbleMenu.d.ts +5 -0
- package/lib/components/bubble/PickLink.d.ts +6 -0
- package/lib/components/bubble/TextTurnInto.d.ts +5 -0
- package/lib/components/bubble/icon.d.ts +4 -0
- package/lib/components/color/ColorDropdown.d.ts +5 -0
- package/lib/components/color/ColorItem.d.ts +5 -0
- package/lib/components/color/ColorSubMenu.d.ts +7 -0
- package/lib/components/color/HighlightButton.d.ts +15 -0
- package/lib/components/color/TextButton.d.ts +9 -0
- package/lib/components/color/constant.d.ts +12 -0
- package/lib/components/color/icon.d.ts +2 -0
- package/lib/components/color/type.d.ts +9 -0
- package/lib/components/color/utils.d.ts +5 -0
- package/lib/components/extensions/Indent.d.ts +16 -0
- package/lib/components/extensions/emoji-picker/EmojiList.d.ts +8 -0
- package/lib/components/extensions/emoji-picker/EmojiPicker.d.ts +14 -0
- package/lib/components/extensions/emoji-picker/emoji-suggestion.d.ts +3 -0
- package/lib/components/extensions/file-handle.d.ts +19 -0
- package/lib/components/extensions/global-block-background.d.ts +2 -0
- package/lib/components/extensions/index.d.ts +51 -0
- package/lib/components/extensions/mention/MentionList.d.ts +11 -0
- package/lib/components/extensions/mention/mentionSuggestion.d.ts +11 -0
- package/lib/components/extensions/mention/types.d.ts +12 -0
- package/lib/components/extensions/slash-command/MenuListGroup.d.ts +9 -0
- package/lib/components/extensions/slash-command/SlashMenu.d.ts +11 -0
- package/lib/components/extensions/slash-command/items.d.ts +3 -0
- package/lib/components/extensions/slash-command/slash-command.d.ts +7 -0
- package/lib/components/extensions/slash-command/suggestion.d.ts +3 -0
- package/lib/components/icons.d.ts +5 -0
- package/lib/components/node/attachment/AttachmentNode.d.ts +38 -0
- package/lib/components/node/attachment/AttachmentNodeView.d.ts +3 -0
- package/lib/components/node/attachment/AttachmentUploadNode.d.ts +37 -0
- package/lib/components/node/attachment/AttachmentUploadNodeView.d.ts +3 -0
- package/lib/components/node/attachment/context.d.ts +16 -0
- package/lib/components/node/attachment/index.d.ts +3 -0
- package/lib/components/node/image/CustomImage.d.ts +1 -0
- package/lib/components/node/image/ImageNodeView.d.ts +5 -0
- package/lib/components/node/image-upload/drop-zone-content.d.ts +4 -0
- package/lib/components/node/image-upload/hooks.d.ts +7 -0
- package/lib/components/node/image-upload/image-drop-area.d.ts +15 -0
- package/lib/components/node/image-upload/image-preview.d.ts +13 -0
- package/lib/components/node/image-upload/image-upload-node-extension.d.ts +58 -0
- package/lib/components/node/image-upload/image-upload-node-view.d.ts +2 -0
- package/lib/components/node/image-upload/index.d.ts +1 -0
- package/lib/components/node/image-upload/types.d.ts +66 -0
- package/lib/components/node/table-node/AddRowButton.d.ts +5 -0
- package/lib/components/node/table-node/CustomTable.d.ts +1 -0
- package/lib/components/node/table-node/TableAddRowColumnHandle.d.ts +7 -0
- package/lib/components/node/table-node/TableHandle.d.ts +4 -0
- package/lib/components/node/table-node/TableMenuHandle.d.ts +7 -0
- package/lib/components/node/table-node/TableSelectionOverlay.d.ts +7 -0
- package/lib/components/node/table-node/plugins/add-row-plugin.d.ts +9 -0
- package/lib/components/node/table-node/plugins/plugin-keys.d.ts +6 -0
- package/lib/components/node/table-node/plugins/table-menu-handle-plugin.d.ts +33 -0
- package/lib/components/node/table-node/plugins/table-selection-overlay.d.ts +10 -0
- package/lib/components/node/table-node/popover/AlignmentContent.d.ts +4 -0
- package/lib/components/node/table-node/popover/CellMenuPopover.d.ts +4 -0
- package/lib/components/node/table-node/popover/Colors.d.ts +4 -0
- package/lib/components/node/table-node/popover/TableMenuItem.d.ts +11 -0
- package/lib/components/node/table-node/popover/TableMenuPopover.d.ts +9 -0
- package/lib/components/node/table-node/table-kit.d.ts +2 -0
- package/lib/components/node/table-node/utils/getActiveTableCellAlignment.d.ts +14 -0
- package/lib/components/node/table-node/utils/getTableColumnMeta.d.ts +15 -0
- package/lib/components/node/table-node/utils/getTableContext.d.ts +16 -0
- package/lib/components/node/table-node/utils/index.d.ts +17 -0
- package/lib/components/node/table-node/utils/table.d.ts +51 -0
- package/lib/components/node/toc-node/TableOfContentsNode.d.ts +13 -0
- package/lib/components/node/toc-node/TocNodeView.d.ts +9 -0
- package/lib/components/toc-sidebar/TocPopover.d.ts +6 -0
- package/lib/components/toc-sidebar/TocSidebar.d.ts +9 -0
- package/lib/components/toc-sidebar/TocSidebarContent.d.ts +6 -0
- package/lib/components/toc-sidebar/TocUIList.d.ts +8 -0
- package/lib/components/ui/button.d.ts +10 -0
- package/lib/components/ui/command.d.ts +18 -0
- package/lib/components/ui/dialog.d.ts +17 -0
- package/lib/components/ui/dropdown-menu.d.ts +29 -0
- package/lib/components/ui/input-group.d.ts +16 -0
- package/lib/components/ui/input.d.ts +3 -0
- package/lib/components/ui/popover.d.ts +10 -0
- package/lib/components/ui/scroll-area.d.ts +5 -0
- package/lib/components/ui/separator.d.ts +4 -0
- package/lib/components/ui/textarea.d.ts +3 -0
- package/lib/components/ui/tooltip.d.ts +7 -0
- package/lib/i18n/context.d.ts +15 -0
- package/lib/i18n/index.d.ts +2 -0
- package/lib/i18n/messages.d.ts +202 -0
- package/lib/index.cjs +9 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.js +5188 -0
- package/lib/index.js.map +1 -0
- package/lib/lib/aspect-ratio.d.ts +21 -0
- package/lib/lib/collect-file-urls.d.ts +10 -0
- package/lib/lib/tiptap-utils.d.ts +173 -0
- package/lib/lib/use-event.d.ts +1 -0
- package/lib/lib/utils.d.ts +25 -0
- package/lib/styles/colors.css +88 -0
- package/lib/styles/editor.css +268 -0
- package/lib/styles/image-upload.css +256 -0
- package/lib/styles/image.css +113 -0
- package/lib/styles/index.css +18 -0
- package/lib/styles/prosemirror.css +271 -0
- package/lib/styles/table.css +243 -0
- package/lib/styles/tokens.css +66 -0
- package/package.json +114 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/lib/utils.ts","../src/components/ui/button.tsx","../src/components/ui/tooltip.tsx","../src/components/TooltipWrapper.tsx","../src/components/ui/dropdown-menu.tsx","../src/components/ui/scroll-area.tsx","../src/components/icons.tsx","../src/components/node/table-node/popover/TableMenuItem.tsx","../src/components/color/icon.tsx","../src/components/node/table-node/utils/getTableContext.ts","../src/components/node/table-node/utils/table.ts","../src/components/node/table-node/utils/getActiveTableCellAlignment.tsx","../src/i18n/messages.ts","../src/i18n/context.tsx","../src/components/node/table-node/popover/AlignmentContent.tsx","../src/components/color/ColorItem.tsx","../src/components/color/constant.ts","../src/components/color/utils.ts","../src/components/node/table-node/popover/Colors.tsx","../src/components/node/table-node/utils/getTableColumnMeta.ts","../src/components/node/table-node/utils/index.ts","../src/components/node/table-node/popover/TableMenuPopover.tsx","../src/components/color/ColorSubMenu.tsx","../src/components/Draggable/index.tsx","../src/components/ToggleButton.tsx","../src/components/ui/separator.tsx","../src/components/bubble/icon.tsx","../src/components/bubble/ImageBubbleMenu.tsx","../src/components/ui/popover.tsx","../src/components/bubble/PickLink.tsx","../src/components/bubble/IndentButtons.tsx","../src/components/bubble/BubbleMoreOption.tsx","../src/components/color/TextButton.tsx","../src/components/color/HighlightButton.tsx","../src/components/color/ColorDropdown.tsx","../src/components/blocks/typography.tsx","../src/components/blocks/list.tsx","../src/components/blocks/code.tsx","../src/components/bubble/TextTurnInto.tsx","../src/components/bubble/MyBubbleMenu.tsx","../src/components/node/table-node/plugins/table-menu-handle-plugin.ts","../src/components/node/table-node/TableMenuHandle.tsx","../src/components/node/table-node/popover/CellMenuPopover.tsx","../src/components/node/table-node/plugins/table-selection-overlay.ts","../src/components/node/table-node/TableSelectionOverlay.tsx","../src/components/node/table-node/plugins/plugin-keys.ts","../src/components/node/table-node/AddRowButton.tsx","../src/components/node/table-node/plugins/add-row-plugin.ts","../src/components/node/table-node/TableAddRowColumnHandle.tsx","../src/components/node/table-node/TableHandle.tsx","../src/components/toc-sidebar/TocUIList.tsx","../src/components/toc-sidebar/TocSidebarContent.tsx","../src/components/toc-sidebar/TocPopover.tsx","../src/components/toc-sidebar/TocSidebar.tsx","../src/lib/use-event.ts","../src/lib/aspect-ratio.ts","../src/components/node/image/ImageNodeView.tsx","../src/components/node/image/CustomImage.ts","../src/components/node/attachment/context.tsx","../src/components/node/attachment/AttachmentNodeView.tsx","../src/components/node/attachment/AttachmentNode.ts","../src/components/node/attachment/AttachmentUploadNodeView.tsx","../src/components/node/attachment/AttachmentUploadNode.ts","../src/lib/tiptap-utils.ts","../src/components/node/image-upload/drop-zone-content.tsx","../src/components/node/image-upload/image-drop-area.tsx","../src/components/node/image-upload/image-preview.tsx","../src/components/node/image-upload/hooks.ts","../src/components/node/image-upload/image-upload-node-view.tsx","../src/components/node/image-upload/image-upload-node-extension.ts","../src/components/node/table-node/CustomTable.ts","../src/components/node/table-node/table-kit.ts","../src/components/node/toc-node/TocNodeView.tsx","../src/components/node/toc-node/TableOfContentsNode.ts","../src/components/extensions/file-handle.ts","../src/components/extensions/global-block-background.ts","../src/components/extensions/Indent.ts","../src/components/extensions/emoji-picker/EmojiList.tsx","../src/components/extensions/emoji-picker/EmojiPicker.tsx","../src/components/extensions/emoji-picker/emoji-suggestion.ts","../src/components/ui/input-group.tsx","../src/components/ui/command.tsx","../src/components/extensions/mention/MentionList.tsx","../src/components/extensions/mention/mentionSuggestion.ts","../src/components/extensions/slash-command/slash-command.ts","../src/components/blocks/table.tsx","../src/components/blocks/emoji.tsx","../src/components/blocks/separator.tsx","../src/components/blocks/tableOfContents.tsx","../src/components/blocks/image.tsx","../src/components/blocks/attachment.tsx","../src/components/extensions/slash-command/items.ts","../src/components/extensions/slash-command/MenuListGroup.tsx","../src/components/extensions/slash-command/SlashMenu.tsx","../src/components/extensions/slash-command/suggestion.ts","../src/components/extensions/index.ts","../src/NotionEditor.tsx","../src/lib/collect-file-urls.ts"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\r\nimport { twMerge } from \"tailwind-merge\"\r\nimport { Editor } from '@tiptap/core';\r\nimport { Node as ProseMirrorNode, DOMSerializer } from '@tiptap/pm/model';\r\nimport { NodeSelection, TextSelection } from '@tiptap/pm/state';\r\nimport { computePosition, flip, offset, shift } from '@floating-ui/dom'\r\nimport {\r\n Placement,\r\n Strategy,\r\n Middleware,\r\n VirtualElement\r\n} from '@floating-ui/dom';\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs))\r\n}\r\n\r\n\r\n\r\nexport const uploadFile = async (file: File, onProgress?: (event: { progress: number }) => void, signal?: AbortSignal) => {\r\n const formData = new FormData();\r\n formData.append('file', file);\r\n\r\n // Note: standard fetch cannot track upload progress\r\n // const response = await fetch('https://example.com', {\r\n // method: 'POST',\r\n // body: formData,\r\n // signal: signal, // Handles cancellation\r\n // });\r\n\r\n // if (!response.ok) {\r\n // throw new Error('Upload failed');\r\n // }\r\n\r\n // const data = await response.json();\r\n // return data.url; \r\n\r\n if (onProgress) {\r\n onProgress({ progress: 50 });\r\n onProgress({ progress: 100 });\r\n }\r\n\r\n\r\n // Generate the local object URL\r\n const objectUrl = URL.createObjectURL(file);\r\n\r\n console.log(objectUrl)\r\n\r\n return objectUrl;\r\n}\r\n\r\n\r\n\r\nexport const copySelectedNodeToClipboard = async (editor: Editor | null): Promise<boolean> => {\r\n if (!editor || !editor.state) {\r\n console.warn('Tiptap copy operation failed: Editor instance is uninitialized.');\r\n return false;\r\n }\r\n\r\n const { selection, doc } = editor.state;\r\n let targetNode: ProseMirrorNode | null = null;\r\n\r\n if (selection instanceof NodeSelection) {\r\n targetNode = selection.node;\r\n } else if (selection instanceof TextSelection) {\r\n targetNode = selection.$from.parent;\r\n }\r\n\r\n if (!targetNode || targetNode.type.name === 'doc') {\r\n console.warn('Tiptap copy operation failed: No copyable block node selected.');\r\n return false;\r\n }\r\n\r\n try {\r\n const schema = editor.schema;\r\n const serializer = DOMSerializer.fromSchema(schema);\r\n const domFragment = serializer.serializeNode(targetNode);\r\n\r\n const tempDiv = document.createElement('div');\r\n tempDiv.appendChild(domFragment);\r\n const htmlContent = tempDiv.innerHTML;\r\n\r\n const textContent = targetNode.textContent || '';\r\n\r\n const clipboardData = new ClipboardItem({\r\n 'text/html': new Blob([htmlContent], { type: 'text/html' }),\r\n 'text/plain': new Blob([textContent], { type: 'text/plain' }),\r\n });\r\n\r\n await navigator.clipboard.write([clipboardData]);\r\n return true;\r\n } catch (error) {\r\n console.error('Fatal: Failed to write selected block node to clipboard.', error);\r\n return false;\r\n }\r\n};\r\n\r\n\r\nexport const deleteSelectedNode = (editor: Editor | null): boolean => {\r\n if (!editor || !editor.state) {\r\n console.warn('Tiptap delete operation failed: Editor instance is uninitialized.');\r\n return false;\r\n }\r\n\r\n const { selection } = editor.state;\r\n let from = selection.from;\r\n let to = selection.to;\r\n\r\n if (selection instanceof NodeSelection) {\r\n from = selection.from;\r\n to = selection.to;\r\n }\r\n else if (selection instanceof TextSelection) {\r\n const depth = selection.$from.depth;\r\n\r\n if (depth === 0) {\r\n return false;\r\n }\r\n\r\n from = selection.$from.before(depth);\r\n to = selection.$from.after(depth);\r\n }\r\n\r\n return editor\r\n .chain()\r\n .deleteRange({ from, to })\r\n .focus()\r\n .run();\r\n};\r\n\r\n\r\nexport const duplicateSelectedNode = (editor: Editor | null) => {\r\n if (!editor || !editor.state) {\r\n console.warn('Tiptap delete operation failed: Editor instance is uninitialized.');\r\n return false;\r\n }\r\n const { selection } = editor.state;\r\n\r\n // `node` chỉ tồn tại trên NodeSelection. Hỏi qua instanceof thay vì bỏ qua\r\n // kiểm tra kiểu, nếu không thì mọi selection khác đều đọc ra undefined một\r\n // cách im lặng.\r\n let targetNode: ProseMirrorNode | null =\r\n selection instanceof NodeSelection ? selection.node : null;\r\n let nodePos = selection.from;\r\n\r\n if (!targetNode) {\r\n targetNode = selection.$from.parent;\r\n nodePos = selection.$from.before(selection.$from.depth);\r\n }\r\n\r\n if (targetNode) {\r\n const insertPosition = nodePos + targetNode.nodeSize;\r\n\r\n editor\r\n .chain()\r\n .insertContentAt(insertPosition, targetNode.toJSON())\r\n .focus()\r\n .setTextSelection(insertPosition + 1)\r\n .run();\r\n }\r\n};\r\n\r\n\r\n\r\nexport const setUniversalBlockBackgroundColor = (editor: Editor | null, color: string): boolean => {\r\n if (!editor || !editor.state) return false;\r\n\r\n const { selection } = editor.state;\r\n let targetNodeName: string | null = null;\r\n let nodePos: number | null = null;\r\n\r\n if (selection instanceof NodeSelection) {\r\n targetNodeName = selection.node.type.name;\r\n nodePos = selection.from;\r\n } else if (selection instanceof TextSelection) {\r\n const depth = selection.$from.depth;\r\n if (depth > 0) {\r\n // Intelligently target the direct parent container block node\r\n targetNodeName = selection.$from.parent.type.name;\r\n nodePos = selection.$from.before(depth);\r\n }\r\n }\r\n\r\n // Execute attribute injection if a safe block target is identified\r\n if (targetNodeName && nodePos !== null && targetNodeName !== 'doc') {\r\n return editor\r\n .chain()\r\n .focus()\r\n .updateAttributes(targetNodeName, { universalBgColor: color })\r\n .run();\r\n }\r\n\r\n return false;\r\n};\r\n\r\n\r\n\r\nfunction getSelectionClientRect(editor: Editor | null) {\r\n\r\n if (!editor) return;\r\n\r\n const { from } = editor.state.selection\r\n const { left, right, top, bottom } = editor.view.coordsAtPos(from)\r\n\r\n return new DOMRect(left, top, right - left, bottom - top)\r\n}\r\n\r\n\r\n\r\ninterface UpdatePositionOptions {\r\n editor?: any; // Replace 'any' with your specific Editor type if using Tiptap, Slate, etc.\r\n clientRect?: DOMRect | null;\r\n element: HTMLElement | null;\r\n placement?: Placement;\r\n strategy?: Strategy;\r\n middleware?: Middleware[];\r\n offsetValue?: number | { mainAxis?: number; crossAxis?: number }; // Adjust based on your offset needs\r\n}\r\n\r\n\r\nexport function updatePosition({\r\n editor,\r\n clientRect,\r\n element,\r\n placement = 'bottom-start',\r\n strategy = 'absolute',\r\n middleware = [shift(), flip()],\r\n offsetValue,\r\n}: UpdatePositionOptions): Promise<void> | undefined {\r\n if (!element) {\r\n return;\r\n }\r\n\r\n // Assuming getSelectionClientRect returns a DOMRect or null\r\n const rect = clientRect || (editor ? getSelectionClientRect(editor) : null);\r\n\r\n if (!rect) {\r\n return;\r\n }\r\n\r\n const virtualElement: VirtualElement = {\r\n getBoundingClientRect: () => rect,\r\n };\r\n\r\n const resolvedMiddleware = offsetValue\r\n ? [offset(offsetValue), ...middleware]\r\n : middleware;\r\n\r\n return computePosition(virtualElement, element, {\r\n placement,\r\n strategy,\r\n middleware: resolvedMiddleware,\r\n }).then(({ x, y, strategy: resolvedStrategy }) => {\r\n Object.assign(element.style, {\r\n width: 'max-content',\r\n position: resolvedStrategy,\r\n left: `${x}px`,\r\n top: `${y}px`,\r\n });\r\n });\r\n}","import * as React from \"react\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\nimport { Slot } from \"radix-ui\";\r\nimport { cn } from \"~/lib/utils\";\r\n\r\nconst buttonVariants = cva(\r\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"bg-primary text-primary-foreground hover:bg-primary/80\",\r\n outline:\r\n \"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\r\n secondary:\r\n \"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground\",\r\n ghost:\r\n \"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/90\",\r\n destructive:\r\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\r\n link: \"text-primary underline-offset-4 hover:underline\",\r\n },\r\n size: {\r\n default:\r\n \"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\r\n xs: \"h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\r\n sm: \"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\r\n lg: \"h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\r\n icon: \"size-8\",\r\n \"icon-xs\":\r\n \"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3\",\r\n \"icon-sm\":\r\n \"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg\",\r\n \"icon-lg\": \"size-9\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n },\r\n);\r\n\r\nfunction Button({\r\n className,\r\n variant = \"default\",\r\n size = \"default\",\r\n asChild = false,\r\n ...props\r\n}: React.ComponentProps<\"button\"> &\r\n VariantProps<typeof buttonVariants> & {\r\n asChild?: boolean;\r\n }) {\r\n const Comp = asChild ? Slot.Root : \"button\";\r\n\r\n return (\r\n <Comp\r\n data-slot=\"button\"\r\n data-variant={variant}\r\n data-size={size}\r\n className={cn(buttonVariants({ variant, size, className }))}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nexport { Button, buttonVariants };\r\n","import * as React from \"react\"\r\nimport { Tooltip as TooltipPrimitive } from \"radix-ui\"\r\n\r\nimport { cn } from \"~/lib/utils\"\r\n\r\nfunction TooltipProvider({\r\n delayDuration = 0,\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\r\n return (\r\n <TooltipPrimitive.Provider\r\n data-slot=\"tooltip-provider\"\r\n delayDuration={delayDuration}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction Tooltip({\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Root>) {\r\n return <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\r\n}\r\n\r\nfunction TooltipTrigger({\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {\r\n return <TooltipPrimitive.Trigger data-slot=\"tooltip-trigger\" {...props} />\r\n}\r\n\r\nfunction TooltipContent({\r\n className,\r\n sideOffset = 0,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof TooltipPrimitive.Content>) {\r\n return (\r\n <TooltipPrimitive.Portal>\r\n <TooltipPrimitive.Content\r\n data-slot=\"tooltip-content\"\r\n sideOffset={sideOffset}\r\n className={cn(\r\n \"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 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 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <TooltipPrimitive.Arrow className=\"z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground\" />\r\n </TooltipPrimitive.Content>\r\n </TooltipPrimitive.Portal>\r\n )\r\n}\r\n\r\nexport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }\r\n","import { Tooltip, TooltipTrigger, TooltipContent } from \"./ui/tooltip\";\r\nimport React from \"react\";\r\n\r\ninterface PropsType {\r\n children: React.ReactNode;\r\n content: string | undefined;\r\n showTooltip?: boolean;\r\n className?: string;\r\n}\r\n\r\nconst TooltipWrapper = ({\r\n children,\r\n content,\r\n className,\r\n showTooltip = true,\r\n}: PropsType) => {\r\n return (\r\n <Tooltip>\r\n <TooltipTrigger asChild>{children}</TooltipTrigger>\r\n {showTooltip && content && (\r\n <TooltipContent className={className}>\r\n <p>{content}</p>\r\n </TooltipContent>\r\n )}\r\n </Tooltip>\r\n );\r\n};\r\n\r\nexport default TooltipWrapper;\r\n","import * as React from \"react\";\r\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\r\n\r\nimport { cn } from \"~/lib/utils\";\r\nimport { CheckIcon, ChevronRightIcon } from \"lucide-react\";\r\n\r\nfunction DropdownMenu({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {\r\n return <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuPortal({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {\r\n return (\r\n <DropdownMenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />\r\n );\r\n}\r\n\r\nfunction DropdownMenuTrigger({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\r\n return (\r\n <DropdownMenuPrimitive.Trigger\r\n data-slot=\"dropdown-menu-trigger\"\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuContent({\r\n className,\r\n align = \"start\",\r\n sideOffset = 4,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {\r\n return (\r\n <DropdownMenuPrimitive.Portal>\r\n <DropdownMenuPrimitive.Content\r\n data-slot=\"dropdown-menu-content\"\r\n sideOffset={sideOffset}\r\n align={align}\r\n className={cn(\r\n \"z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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 data-[state=closed]:overflow-hidden data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n </DropdownMenuPrimitive.Portal>\r\n );\r\n}\r\n\r\nfunction DropdownMenuGroup({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {\r\n return (\r\n <DropdownMenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />\r\n );\r\n}\r\n\r\nfunction DropdownMenuItem({\r\n className,\r\n inset,\r\n variant = \"default\",\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {\r\n inset?: boolean;\r\n variant?: \"default\" | \"destructive\";\r\n}) {\r\n return (\r\n <DropdownMenuPrimitive.Item\r\n data-slot=\"dropdown-menu-item\"\r\n data-inset={inset}\r\n data-variant={variant}\r\n className={cn(\r\n \"group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuCheckboxItem({\r\n className,\r\n children,\r\n checked,\r\n inset,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & {\r\n inset?: boolean;\r\n}) {\r\n return (\r\n <DropdownMenuPrimitive.CheckboxItem\r\n data-slot=\"dropdown-menu-checkbox-item\"\r\n data-inset={inset}\r\n className={cn(\r\n \"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n className,\r\n )}\r\n checked={checked}\r\n {...props}\r\n >\r\n <span\r\n className=\"pointer-events-none absolute right-2 flex items-center justify-center\"\r\n data-slot=\"dropdown-menu-checkbox-item-indicator\"\r\n >\r\n <DropdownMenuPrimitive.ItemIndicator>\r\n <CheckIcon />\r\n </DropdownMenuPrimitive.ItemIndicator>\r\n </span>\r\n {children}\r\n </DropdownMenuPrimitive.CheckboxItem>\r\n );\r\n}\r\n\r\nfunction DropdownMenuRadioGroup({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {\r\n return (\r\n <DropdownMenuPrimitive.RadioGroup\r\n data-slot=\"dropdown-menu-radio-group\"\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuRadioItem({\r\n className,\r\n children,\r\n inset,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem> & {\r\n inset?: boolean;\r\n}) {\r\n return (\r\n <DropdownMenuPrimitive.RadioItem\r\n data-slot=\"dropdown-menu-radio-item\"\r\n data-inset={inset}\r\n className={cn(\r\n \"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n className,\r\n )}\r\n {...props}\r\n >\r\n <span\r\n className=\"pointer-events-none absolute right-2 flex items-center justify-center\"\r\n data-slot=\"dropdown-menu-radio-item-indicator\"\r\n >\r\n <DropdownMenuPrimitive.ItemIndicator>\r\n <CheckIcon />\r\n </DropdownMenuPrimitive.ItemIndicator>\r\n </span>\r\n {children}\r\n </DropdownMenuPrimitive.RadioItem>\r\n );\r\n}\r\n\r\nfunction DropdownMenuLabel({\r\n className,\r\n inset,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {\r\n inset?: boolean;\r\n}) {\r\n return (\r\n <DropdownMenuPrimitive.Label\r\n data-slot=\"dropdown-menu-label\"\r\n data-inset={inset}\r\n className={cn(\r\n \"px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuSeparator({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\r\n return (\r\n <DropdownMenuPrimitive.Separator\r\n data-slot=\"dropdown-menu-separator\"\r\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuShortcut({\r\n className,\r\n ...props\r\n}: React.ComponentProps<\"span\">) {\r\n return (\r\n <span\r\n data-slot=\"dropdown-menu-shortcut\"\r\n className={cn(\r\n \"ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nfunction DropdownMenuSub({\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\r\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\r\n}\r\n\r\nfunction DropdownMenuSubTrigger({\r\n className,\r\n inset,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {\r\n inset?: boolean;\r\n}) {\r\n return (\r\n <DropdownMenuPrimitive.SubTrigger\r\n data-slot=\"dropdown-menu-sub-trigger\"\r\n data-inset={inset}\r\n className={cn(\r\n \"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\r\n className,\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <ChevronRightIcon className=\"ml-auto\" />\r\n </DropdownMenuPrimitive.SubTrigger>\r\n );\r\n}\r\n\r\nfunction DropdownMenuSubContent({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\r\n return (\r\n <DropdownMenuPrimitive.SubContent\r\n data-slot=\"dropdown-menu-sub-content\"\r\n className={cn(\r\n \"z-50 min-w-24 origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n );\r\n}\r\n\r\nexport {\r\n DropdownMenu,\r\n DropdownMenuPortal,\r\n DropdownMenuTrigger,\r\n DropdownMenuContent,\r\n DropdownMenuGroup,\r\n DropdownMenuLabel,\r\n DropdownMenuItem,\r\n DropdownMenuCheckboxItem,\r\n DropdownMenuRadioGroup,\r\n DropdownMenuRadioItem,\r\n DropdownMenuSeparator,\r\n DropdownMenuShortcut,\r\n DropdownMenuSub,\r\n DropdownMenuSubTrigger,\r\n DropdownMenuSubContent,\r\n};\r\n","import * as React from \"react\"\r\nimport { ScrollArea as ScrollAreaPrimitive } from \"radix-ui\"\r\n\r\nimport { cn } from \"~/lib/utils\"\r\n\r\nfunction ScrollArea({\r\n className,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {\r\n return (\r\n <ScrollAreaPrimitive.Root\r\n data-slot=\"scroll-area\"\r\n className={cn(\"relative\", className)}\r\n {...props}\r\n >\r\n <ScrollAreaPrimitive.Viewport\r\n data-slot=\"scroll-area-viewport\"\r\n className=\"size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1\"\r\n >\r\n {children}\r\n </ScrollAreaPrimitive.Viewport>\r\n <ScrollBar />\r\n <ScrollAreaPrimitive.Corner />\r\n </ScrollAreaPrimitive.Root>\r\n )\r\n}\r\n\r\nfunction ScrollBar({\r\n className,\r\n orientation = \"vertical\",\r\n ...props\r\n}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {\r\n return (\r\n <ScrollAreaPrimitive.ScrollAreaScrollbar\r\n data-slot=\"scroll-area-scrollbar\"\r\n data-orientation={orientation}\r\n orientation={orientation}\r\n className={cn(\r\n \"flex touch-none p-px transition-colors select-none data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n <ScrollAreaPrimitive.ScrollAreaThumb\r\n data-slot=\"scroll-area-thumb\"\r\n className=\"relative flex-1 rounded-full bg-border\"\r\n />\r\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\r\n )\r\n}\r\n\r\nexport { ScrollArea, ScrollBar }\r\n","export const InsertLeftColumn = (props: any) => {\r\n return (\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n {...props}\r\n >\r\n <path\r\n fill-rule=\"evenodd\"\r\n clip-rule=\"evenodd\"\r\n d=\"M19 2C20.6569 2 22 3.34315 22 5V19C22 20.6569 20.6569 22 19 22H15C13.3431 22 12 20.6569 12 19V5C12 3.34315 13.3431 2 15 2H19ZM14 19C14 19.5523 14.4477 20 15 20H19C19.5523 20 20 19.5523 20 19V13H14V19ZM15 4C14.4477 4 14 4.44772 14 5V11H20V5C20 4.44772 19.5523 4 19 4H15Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M6 8C6.55228 8 7 8.44772 7 9V11H9C9.55228 11 10 11.4477 10 12C10 12.5523 9.55228 13 9 13H7V15C7 15.5523 6.55228 16 6 16C5.44772 16 5 15.5523 5 15V13H3C2.44772 13 2 12.5523 2 12C2 11.4477 2.44772 11 3 11H5V9C5 8.44772 5.44772 8 6 8Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n\r\nexport const InsertRightColumn = (props: any) => {\r\n return (\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n {...props}\r\n >\r\n <path\r\n fill-rule=\"evenodd\"\r\n clip-rule=\"evenodd\"\r\n d=\"M9 2C10.6569 2 12 3.34315 12 5V19C12 20.6569 10.6569 22 9 22H5C3.34315 22 2 20.6569 2 19V5C2 3.34315 3.34315 2 5 2H9ZM4 19C4 19.5523 4.44772 20 5 20H9C9.55228 20 10 19.5523 10 19V13H4V19ZM5 4C4.44772 4 4 4.44772 4 5V11H10V5C10 4.44772 9.55228 4 9 4H5Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M18 8C18.5523 8 19 8.44772 19 9V11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H19V15C19 15.5523 18.5523 16 18 16C17.4477 16 17 15.5523 17 15V13H15C14.4477 13 14 12.5523 14 12C14 11.4477 14.4477 11 15 11H17V9C17 8.44772 17.4477 8 18 8Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n\r\nexport const InsertRowAbove = (props: any) => {\r\n return (\r\n <svg\r\n {...props}\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n fill-rule=\"evenodd\"\r\n clip-rule=\"evenodd\"\r\n d=\"M19 12C20.6569 12 22 13.3431 22 15V19C22 20.6569 20.6569 22 19 22H5C3.34315 22 2 20.6569 2 19V15C2 13.3431 3.34315 12 5 12H19ZM5 14C4.44772 14 4 14.4477 4 15V19C4 19.5523 4.44772 20 5 20H11V14H5ZM13 20H19C19.5523 20 20 19.5523 20 19V15C20 14.4477 19.5523 14 19 14H13V20Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M12 2C12.5523 2 13 2.44772 13 3V5H15C15.5523 5 16 5.44772 16 6C16 6.55228 15.5523 7 15 7H13V9C13 9.55228 12.5523 10 12 10C11.4477 10 11 9.55228 11 9V7H9C8.44772 7 8 6.55228 8 6C8 5.44772 8.44772 5 9 5H11V3C11 2.44772 11.4477 2 12 2Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n\r\nexport const InsertRowBelow = (props: any) => {\r\n return (\r\n <svg\r\n {...props}\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n d=\"M12 14C12.5523 14 13 14.4477 13 15V17H15C15.5523 17 16 17.4477 16 18C16 18.5523 15.5523 19 15 19H13V21C13 21.5523 12.5523 22 12 22C11.4477 22 11 21.5523 11 21V19H9C8.44772 19 8 18.5523 8 18C8 17.4477 8.44772 17 9 17H11V15C11 14.4477 11.4477 14 12 14Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n fill-rule=\"evenodd\"\r\n clip-rule=\"evenodd\"\r\n d=\"M19 2C20.6569 2 22 3.34315 22 5V9C22 10.6569 20.6569 12 19 12H5C3.34315 12 2 10.6569 2 9V5C2 3.34315 3.34315 2 5 2H19ZM5 4C4.44772 4 4 4.44772 4 5V9C4 9.55228 4.44772 10 5 10H11V4H5ZM13 10H19C19.5523 10 20 9.55228 20 9V5C20 4.44772 19.5523 4 19 4H13V10Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n\r\nexport const TableMergeIcon = (props: any) => {\r\n return (\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n {...props}\r\n >\r\n <path\r\n d=\"M10 16C10.5177 16 10.9438 16.3933 10.9951 16.8975L11 17V19C11 20.6569 9.65685 22 8 22H4C2.34315 22 1 20.6569 1 19V17C1 16.4477 1.44772 16 2 16C2.55228 16 3 16.4477 3 17V19C3 19.5523 3.44772 20 4 20H8C8.55228 20 9 19.5523 9 19V17L9.00488 16.8975C9.05621 16.3933 9.48232 16 10 16Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M22 16C22.5177 16 22.9438 16.3933 22.9951 16.8975L23 17V19C23 20.6569 21.6569 22 20 22H16C14.3431 22 13 20.6569 13 19V17C13 16.4477 13.4477 16 14 16C14.5523 16 15 16.4477 15 17V19C15 19.5523 15.4477 20 16 20H20C20.5523 20 21 19.5523 21 19V17L21.0049 16.8975C21.0562 16.3933 21.4823 16 22 16Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M5.29297 8.29297C5.68349 7.90244 6.31651 7.90244 6.70703 8.29297L9.70703 11.293L9.77539 11.3691C10.0957 11.7619 10.0731 12.3409 9.70703 12.707L6.70703 15.707C6.31651 16.0975 5.68349 16.0975 5.29297 15.707C4.90245 15.3165 4.90245 14.6835 5.29297 14.293L6.58594 13H3C2.44772 13 2 12.5523 2 12C2 11.4477 2.44772 11 3 11H6.58594L5.29297 9.70703C4.90245 9.31651 4.90245 8.68349 5.29297 8.29297Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M17.293 8.29297C17.6835 7.90245 18.3165 7.90246 18.707 8.29297C19.0976 8.68349 19.0976 9.31651 18.707 9.70703L17.4141 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H17.4141L18.707 14.293C19.0976 14.6835 19.0976 15.3165 18.707 15.707C18.3165 16.0976 17.6835 16.0976 17.293 15.707L14.293 12.707L14.2246 12.6309C13.9043 12.2381 13.9269 11.6591 14.293 11.293L17.293 8.29297Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M8 2C9.65685 2 11 3.34315 11 5V7C11 7.55228 10.5523 8 10 8C9.44771 8 9 7.55228 9 7V5C9 4.44772 8.55228 4 8 4H4C3.44772 4 3 4.44772 3 5V7C3 7.55228 2.55228 8 2 8C1.44772 8 1 7.55228 1 7V5C1 3.34315 2.34315 2 4 2H8Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M20 2C21.6569 2 23 3.34315 23 5V7C23 7.55228 22.5523 8 22 8C21.4477 8 21 7.55228 21 7V5C21 4.44772 20.5523 4 20 4H16C15.4477 4 15 4.44772 15 5V7C15 7.55228 14.5523 8 14 8C13.4477 8 13 7.55228 13 7V5C13 3.34315 14.3431 2 16 2H20Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n","import { LucideIcon } from \"lucide-react\";\r\nimport { DropdownMenuItem } from \"~/components/ui/dropdown-menu\";\r\nimport { cn } from \"~/lib/utils\";\r\nimport { ITEM_CLASSNAME } from \"./TableMenuPopover\";\r\n\r\ninterface MenuItemProps {\r\n icon: LucideIcon | any;\r\n label: string;\r\n onClick?: () => void;\r\n variant?: \"default\" | \"destructive\";\r\n disabled?: boolean;\r\n className?: string\r\n}\r\n\r\n// Reusable item wrapper\r\nexport const TableMenuItem = ({\r\n icon: Icon,\r\n label,\r\n onClick,\r\n variant = \"default\",\r\n disabled = false,\r\n className\r\n}: MenuItemProps) => {\r\n return (\r\n <DropdownMenuItem\r\n className={cn(ITEM_CLASSNAME, className, {\r\n \"text-destructive hover:bg-destructive/10 focus:bg-destructive/10 focus:text-destructive\":\r\n variant === \"destructive\",\r\n \"text-muted-foreground hover:bg-accent hover:text-accent-foreground\":\r\n variant === \"default\",\r\n })}\r\n onClick={onClick}\r\n disabled={disabled}\r\n >\r\n <Icon className=\"size-4 shrink-0\" />\r\n <span>{label}</span>\r\n </DropdownMenuItem>\r\n );\r\n};\r\n","export const TextIcon = (props: any) => {\r\n return (\r\n <svg\r\n {...props}\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n fillRule=\"evenodd\"\r\n clipRule=\"evenodd\"\r\n d=\"M12.8944 5.55279C12.725 5.214 12.3787 5 12 5C11.6212 5 11.2749 5.214 11.1055 5.55279L5.10555 17.5528C4.85856 18.0468 5.05878 18.6474 5.55276 18.8944C6.04674 19.1414 6.64741 18.9412 6.8944 18.4472L8.64957 14.9369C8.75862 14.9777 8.87671 15 9 15H15C15.1233 15 15.2413 14.9777 15.3504 14.9369L17.1055 18.4472C17.3525 18.9412 17.9532 19.1414 18.4472 18.8944C18.9412 18.6474 19.1414 18.0468 18.8944 17.5528L12.8944 5.55279ZM14.3819 13L12 8.23607L9.61801 13H14.3819Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n\r\nexport const AlignmentIcon = (props: any) => {\r\n return (\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n {...props}\r\n >\r\n <path\r\n d=\"M3 16C3.55228 16 4 16.4477 4 17V19L4.00488 19.0986C4.02757 19.3276 4.12883 19.5429 4.29297 19.707C4.48051 19.8946 4.73478 20 5 20H7C7.55228 20 8 20.4477 8 21C8 21.5523 7.55228 22 7 22H5C4.20435 22 3.44151 21.6837 2.87891 21.1211C2.3163 20.5585 2 19.7957 2 19V17C2 16.4477 2.44772 16 3 16Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M21 16C21.5523 16 22 16.4477 22 17V19C22 19.7957 21.6837 20.5585 21.1211 21.1211C20.5585 21.6837 19.7957 22 19 22H17C16.4477 22 16 21.5523 16 21C16 20.4477 16.4477 20 17 20H19C19.2652 20 19.5195 19.8946 19.707 19.707C19.8946 19.5195 20 19.2652 20 19V17C20 16.4477 20.4477 16 21 16Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M7 16C7.55228 16 8 16.4477 8 17C8 17.5523 7.55228 18 7 18C6.44772 18 6 17.5523 6 17C6 16.4477 6.44772 16 7 16Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M12 16C12.5523 16 13 16.4477 13 17C13 17.5523 12.5523 18 12 18C11.4477 18 11 17.5523 11 17C11 16.4477 11.4477 16 12 16Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M17 16C17.5523 16 18 16.4477 18 17C18 17.5523 17.5523 18 17 18C16.4477 18 16 17.5523 16 17C16 16.4477 16.4477 16 17 16Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M7 11C7.55228 11 8 11.4477 8 12C8 12.5523 7.55228 13 7 13C6.44772 13 6 12.5523 6 12C6 11.4477 6.44772 11 7 11Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M12 11C12.5523 11 13 11.4477 13 12C13 12.5523 12.5523 13 12 13C11.4477 13 11 12.5523 11 12C11 11.4477 11.4477 11 12 11Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M17 11C17.5523 11 18 11.4477 18 12C18 12.5523 17.5523 13 17 13C16.4477 13 16 12.5523 16 12C16 11.4477 16.4477 11 17 11Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M7 2C7.55228 2 8 2.44772 8 3C8 3.55228 7.55228 4 7 4H5C4.73478 4 4.4805 4.10543 4.29297 4.29297C4.10543 4.4805 4 4.73478 4 5V7C4 7.55228 3.55228 8 3 8C2.44772 8 2 7.55228 2 7V5C2 4.20435 2.3163 3.44152 2.87891 2.87891C3.44152 2.3163 4.20435 2 5 2H7Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M7 6C7.55228 6 8 6.44772 8 7C8 7.55228 7.55228 8 7 8C6.44772 8 6 7.55228 6 7C6 6.44772 6.44772 6 7 6Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M12 6C12.5523 6 13 6.44772 13 7C13 7.55228 12.5523 8 12 8C11.4477 8 11 7.55228 11 7C11 6.44772 11.4477 6 12 6Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M17 6C17.5523 6 18 6.44772 18 7C18 7.55228 17.5523 8 17 8C16.4477 8 16 7.55228 16 7C16 6.44772 16.4477 6 17 6Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M19 2C19.7957 2 20.5585 2.3163 21.1211 2.87891C21.6837 3.44151 22 4.20435 22 5V7C22 7.55228 21.5523 8 21 8C20.4477 8 20 7.55228 20 7V5C20 4.73478 19.8946 4.48051 19.707 4.29297C19.5429 4.12883 19.3276 4.02757 19.0986 4.00488L19 4H17C16.4477 4 16 3.55228 16 3C16 2.44772 16.4477 2 17 2H19Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n","import { EditorState } from '@tiptap/pm/state'\r\nimport { TableMap } from '@tiptap/pm/tables'\r\nimport { ResolvedPos, Node as ProsemirrorNode } from '@tiptap/pm/model'\r\n\r\ninterface CellSelection extends Selection {\r\n $anchor: any\r\n anchorCell?: ResolvedPos\r\n}\r\n\r\nexport interface TableActionContext {\r\n tableNode: ProsemirrorNode\r\n tablePos: number\r\n map: TableMap\r\n cellPos: number\r\n cellRect: { top: number; bottom: number; left: number; right: number }\r\n}\r\n\r\nexport const getTableContext = (state: EditorState): TableActionContext | null => {\r\n const selection = state.selection as unknown as CellSelection\r\n let cellPos: number | null = null\r\n\r\n if (selection.anchorCell) {\r\n cellPos = selection.anchorCell.pos\r\n } else {\r\n const $pos = selection.$anchor\r\n for (let d = $pos.depth; d > 0; d--) {\r\n const nodeName = $pos.node(d).type.name\r\n if (nodeName === 'tableCell' || nodeName === 'tableHeader') {\r\n cellPos = $pos.before(d)\r\n break\r\n }\r\n }\r\n }\r\n\r\n if (cellPos === null) return null\r\n\r\n const $cellPos = state.doc.resolve(cellPos)\r\n let tableNode: ProsemirrorNode | null = null\r\n let tablePos = -1\r\n\r\n for (let d = $cellPos.depth; d > 0; d--) {\r\n if ($cellPos.node(d).type.name === 'table') {\r\n tableNode = $cellPos.node(d)\r\n tablePos = $cellPos.before(d)\r\n break\r\n }\r\n }\r\n\r\n if (!tableNode || tablePos === -1) return null\r\n\r\n const map = TableMap.get(tableNode)\r\n const localCellPos = cellPos - tablePos - 1\r\n const cellRect = map.findCell(localCellPos)\r\n\r\n return { tableNode, tablePos, map, cellPos, cellRect }\r\n}\r\n","import { Editor } from \"@tiptap/react\";\r\nimport { CellSelection, moveTableColumn, moveTableRow, TableMap } from '@tiptap/pm/tables'\r\nimport { Fragment, Node as ProsemirrorNode } from '@tiptap/pm/model'\r\nimport { getTableContext } from \"./getTableContext\";\r\n\r\n/**\r\n * Inserts a blank row at the very bottom of a table without changing selection.\r\n */\r\nexport function insertRowAtBottom(editor: Editor, tablePos: number, trackHistory = true) {\r\n const { state, view } = editor;\r\n const { tr, schema } = state;\r\n const tableNode = state.doc.nodeAt(tablePos);\r\n\r\n if (!tableNode || tableNode.type.name !== \"table\") return false;\r\n\r\n const firstRow = tableNode.firstChild;\r\n if (!firstRow) return false;\r\n\r\n const cells = [];\r\n for (let i = 0; i < firstRow.childCount; i++) {\r\n const cell = schema.nodes.tableCell.createAndFill();\r\n if (cell) cells.push(cell);\r\n }\r\n\r\n const newRow = schema.nodes.tableRow.create(null, Fragment.from(cells));\r\n const insertPos = tablePos + tableNode.nodeSize - 1;\r\n\r\n tr.insert(insertPos, newRow);\r\n tr.setMeta(\"addToHistory\", trackHistory);\r\n view.dispatch(tr);\r\n return true;\r\n}\r\n\r\n/**\r\n * Deletes the last row of a table if it is completely empty.\r\n */\r\nexport function deleteEmptyRowFromBottom(editor: Editor, tablePos: number, trackHistory = true) {\r\n const { state, view } = editor;\r\n const { tr } = state;\r\n const tableNode = state.doc.nodeAt(tablePos);\r\n\r\n if (!tableNode || tableNode.type.name !== \"table\" || tableNode.childCount <= 1) return false;\r\n\r\n const lastRowNode = tableNode.lastChild;\r\n if (!lastRowNode) return false;\r\n\r\n // Verify row emptiness\r\n let isEmpty = true;\r\n lastRowNode.forEach((cell) => {\r\n if (cell.textContent.trim() !== \"\") isEmpty = false;\r\n });\r\n\r\n if (!isEmpty) return false;\r\n\r\n const deletePos = tablePos + tableNode.nodeSize - lastRowNode.nodeSize - 1;\r\n tr.delete(deletePos, deletePos + lastRowNode.nodeSize);\r\n tr.setMeta(\"addToHistory\", trackHistory);\r\n view.dispatch(tr);\r\n return true;\r\n}\r\n\r\n\r\n/**\r\n * Appends a structural blank column at the far right edge of a targeted table pos.\r\n */\r\nexport function insertColumnAtRight(editor: Editor, tablePos: number, addToHistory = true): boolean {\r\n const { state, view } = editor;\r\n let { tr } = state;\r\n\r\n const tableNode = state.doc.nodeAt(tablePos);\r\n if (!tableNode || tableNode.type.name !== \"table\") return false;\r\n\r\n const map = TableMap.get(tableNode);\r\n\r\n const lastColIndex = map.width;\r\n\r\n const cellPos = map.positionAt(0, lastColIndex, tableNode);\r\n const absoluteCellPos = tablePos + cellPos;\r\n\r\n try {\r\n editor.chain()\r\n .focus()\r\n .setTextSelection(absoluteCellPos)\r\n .addColumnAfter()\r\n .run();\r\n\r\n setColumnWidth(\r\n editor,\r\n tablePos,\r\n lastColIndex,\r\n 200\r\n );\r\n\r\n if (!addToHistory) {\r\n tr.setMeta(\"addToHistory\", false);\r\n }\r\n\r\n return true;\r\n } catch (error) {\r\n console.error(\"Failed to insert column at the right edge:\", error);\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Removes the column on the far right edge if it contains no content.\r\n */\r\nexport function deleteEmptyColumnFromRight(editor: Editor, tablePos: number, trackHistory = true) {\r\n const { state, view } = editor;\r\n const { tr } = state;\r\n const tableNode = state.doc.nodeAt(tablePos);\r\n\r\n if (!tableNode || tableNode.type.name !== \"table\") return false;\r\n\r\n const firstRow = tableNode.firstChild;\r\n if (!firstRow || firstRow.childCount <= 1) return false;\r\n\r\n const lastColumnIndex = firstRow.childCount - 1;\r\n let isColumnEmpty = true;\r\n\r\n // Double-check emptiness across all stacked rows before deletion\r\n tableNode.forEach((rowNode) => {\r\n const lastCell = rowNode.child(lastColumnIndex);\r\n if (lastCell && lastCell.textContent.trim() !== \"\") {\r\n isColumnEmpty = false;\r\n }\r\n });\r\n\r\n if (!isColumnEmpty) return false;\r\n\r\n const reverseRows: { pos: number; size: number }[] = [];\r\n tableNode.forEach((rowNode, rowOffset) => {\r\n let cellOffset = 1;\r\n for (let i = 0; i < lastColumnIndex; i++) {\r\n cellOffset += rowNode.child(i).nodeSize;\r\n }\r\n const targetCell = rowNode.child(lastColumnIndex);\r\n reverseRows.unshift({\r\n pos: tablePos + 1 + rowOffset + cellOffset,\r\n size: targetCell.nodeSize\r\n });\r\n });\r\n\r\n reverseRows.forEach(({ pos, size }) => {\r\n tr.delete(pos, pos + size);\r\n });\r\n\r\n tr.setMeta(\"addToHistory\", trackHistory);\r\n view.dispatch(tr);\r\n return true;\r\n}\r\n\r\n\r\n\r\nfunction setColumnWidth(\r\n editor: Editor,\r\n tablePos: number,\r\n columnIndex: number,\r\n width: number\r\n) {\r\n const { state, view } = editor;\r\n let tr = state.tr;\r\n\r\n const table = state.doc.nodeAt(tablePos);\r\n\r\n if (!table) return false;\r\n\r\n const map = TableMap.get(table);\r\n\r\n for (let row = 0; row < map.height; row++) {\r\n const cellPos = map.positionAt(row, columnIndex, table);\r\n const absolutePos = tablePos + cellPos;\r\n\r\n const cell = tr.doc.nodeAt(absolutePos);\r\n\r\n if (!cell) continue;\r\n\r\n tr.setNodeMarkup(absolutePos, undefined, {\r\n ...cell.attrs,\r\n colwidth: [width],\r\n });\r\n }\r\n\r\n view.dispatch(tr);\r\n\r\n return true;\r\n}\r\n/**\r\n * Moves the currently active table column left or right.\r\n */\r\nexport const moveActiveColumn = (editor: Editor, direction: 'left' | 'right'): boolean => {\r\n return editor.chain().focus().command(({ state, dispatch }) => {\r\n const context = getTableContext(state)\r\n if (!context) return false\r\n\r\n const currentColumnIndex = context.cellRect.left\r\n const targetColumnIndex = direction === 'left' ? currentColumnIndex - 1 : currentColumnIndex + 1\r\n\r\n if (targetColumnIndex >= 0 && targetColumnIndex < context.map.width) {\r\n if (dispatch) {\r\n return moveTableColumn({ from: currentColumnIndex, to: targetColumnIndex })(state, dispatch)\r\n }\r\n return true\r\n }\r\n return false\r\n }).run()\r\n}\r\n\r\n/**\r\n * Moves the currently active table row up or down.\r\n */\r\nexport const moveActiveRow = (editor: Editor, direction: 'up' | 'down'): boolean => {\r\n return editor.chain().focus().command(({ state, dispatch }) => {\r\n const context = getTableContext(state)\r\n if (!context) return false\r\n\r\n const currentRowIndex = context.cellRect.top\r\n const targetRowIndex = direction === 'up' ? currentRowIndex - 1 : currentRowIndex + 1\r\n\r\n if (targetRowIndex >= 0 && targetRowIndex < context.map.height) {\r\n if (dispatch) {\r\n return moveTableRow({ from: currentRowIndex, to: targetRowIndex })(state, dispatch)\r\n }\r\n return true\r\n }\r\n return false\r\n }).run()\r\n}\r\n\r\n/**\r\n * Duplicates the active row, copying its structure and contents cleanly.\r\n */\r\nexport const duplicateActiveRow = (editor: Editor): boolean => {\r\n return editor.chain().focus().command(({ state, dispatch }) => {\r\n const context = getTableContext(state)\r\n if (!context || !dispatch) return false\r\n\r\n const { tableNode, tablePos, cellRect } = context\r\n const currentRowIndex = cellRect.top\r\n const tr = state.tr\r\n\r\n // 1. Capture content of the current row node\r\n const targetRowNode = tableNode.child(currentRowIndex)\r\n const duplicatedRow = targetRowNode.copy(Fragment.fromJSON(state.schema, targetRowNode.content.toJSON()))\r\n\r\n // 2. Find absolute character index where the current row ends\r\n let insertRowOffset = tablePos + 1\r\n for (let i = 0; i <= currentRowIndex; i++) {\r\n insertRowOffset += tableNode.child(i).nodeSize\r\n }\r\n\r\n // 3. Inject the duplicated row node inside the structural transaction\r\n tr.insert(insertRowOffset, duplicatedRow)\r\n dispatch(tr)\r\n return true\r\n }).run()\r\n}\r\n\r\n/**\r\n * Duplicates the active column, copying cell nodes down the vertical stack.\r\n */\r\nexport const duplicateActiveColumn = (editor: Editor): boolean => {\r\n return editor.chain().focus().command(({ state, dispatch }) => {\r\n const context = getTableContext(state)\r\n if (!context || !dispatch) return false\r\n\r\n const { tableNode, tablePos, map, cellRect } = context\r\n const currentColumnIndex = cellRect.left\r\n const tr = state.tr\r\n\r\n // 1. Map out structural changes row-by-row in backward order to safeguard mapping offsets\r\n let relativePositionOffset = 0\r\n\r\n for (let r = 0; r < map.height; r++) {\r\n const mapIndex = r * map.width + currentColumnIndex\r\n const localCellPos = map.map[mapIndex]\r\n const cellAbsolutePos = tablePos + 1 + localCellPos\r\n\r\n const originalCellNode = state.doc.nodeAt(cellAbsolutePos)\r\n if (!originalCellNode) continue\r\n\r\n const duplicatedCell = originalCellNode.copy(Fragment.fromJSON(state.schema, originalCellNode.content.toJSON()))\r\n\r\n // Calculate target insertion offset (directly after the original cell ends)\r\n const insertionPoint = cellAbsolutePos + originalCellNode.nodeSize + relativePositionOffset\r\n\r\n tr.insert(insertionPoint, duplicatedCell)\r\n\r\n // Accumulate mapping position offset shift dynamically inside this transaction cycle\r\n relativePositionOffset += duplicatedCell.nodeSize\r\n }\r\n\r\n dispatch(tr)\r\n return true\r\n }).run()\r\n}\r\n\r\n\r\ninterface SelectionWithCells extends Selection {\r\n $anchor: any;\r\n forEachCell?: (callback: (node: any, pos: number) => void) => void\r\n}\r\n\r\ninterface CellStylingOptions {\r\n backgroundColor?: string // e.g., '#ff0000' or 'transparent'\r\n textColor?: string // e.g., '#ffffff'\r\n}\r\n\r\n\r\nexport const setTableCellStyles = (\r\n editor: Editor,\r\n options: CellStylingOptions\r\n): boolean => {\r\n const { backgroundColor, textColor } = options\r\n\r\n return editor\r\n .chain()\r\n .focus()\r\n .command(({ state, tr, dispatch }) => {\r\n const selection = state.selection as unknown as SelectionWithCells\r\n const cellsToUpdate: number[] = []\r\n\r\n // 1. Gather targeted absolute cell coordinates\r\n if (selection instanceof CellSelection || selection.forEachCell) {\r\n // Multi-cell select handle block\r\n selection.forEachCell!((_node, pos) => {\r\n cellsToUpdate.push(pos)\r\n })\r\n } else {\r\n // Single cell text cursor placement fallback\r\n const $pos = selection.$anchor\r\n for (let d = $pos.depth; d > 0; d--) {\r\n const nodeName = $pos.node(d).type.name\r\n if (nodeName === 'tableCell' || nodeName === 'tableHeader') {\r\n cellsToUpdate.push($pos.before(d))\r\n break\r\n }\r\n }\r\n }\r\n\r\n if (cellsToUpdate.length === 0) return false\r\n\r\n if (dispatch) {\r\n cellsToUpdate.forEach((cellPos) => {\r\n const cellNode = tr.doc.nodeAt(cellPos)\r\n if (!cellNode) return\r\n\r\n // 2. Set Background Color (Modifies the TD/TH node attributes directly)\r\n if (backgroundColor !== undefined) {\r\n const currentAttrs = cellNode.attrs\r\n tr.setNodeMarkup(cellPos, undefined, {\r\n ...currentAttrs,\r\n background: backgroundColor === 'transparent' ? null : backgroundColor,\r\n })\r\n }\r\n\r\n // 3. Set Text Color (Modifies internal text nodes within cell boundaries)\r\n if (textColor !== undefined) {\r\n const cellContentStart = cellPos + 1\r\n const cellContentEnd = cellPos + cellNode.nodeSize - 1\r\n\r\n const colorMark = state.schema.marks.textStyle.create({ color: textColor })\r\n\r\n // Remove existing colors within this range to prevent stacked duplicates\r\n tr.removeMark(cellContentStart, cellContentEnd, state.schema.marks.textStyle)\r\n // Inject new style mark across the inner text boundaries safely\r\n tr.addMark(cellContentStart, cellContentEnd, colorMark)\r\n }\r\n })\r\n\r\n dispatch(tr)\r\n return true\r\n }\r\n\r\n return true\r\n })\r\n .run()\r\n}\r\n\r\n\r\ninterface SelectionWithCells extends Selection {\r\n forEachCell?: (callback: (node: any, pos: number) => void) => void\r\n}\r\n\r\ntype HorizontalAlign = 'left' | 'center' | 'right' | 'justify'\r\ntype VerticalAlign = 'top' | 'middle' | 'bottom'\r\n\r\ninterface CellAlignmentOptions {\r\n horizontal?: HorizontalAlign\r\n vertical?: VerticalAlign\r\n}\r\n\r\nexport const setTableCellAlignment = (\r\n editor: Editor,\r\n options: CellAlignmentOptions\r\n): boolean => {\r\n const { horizontal, vertical } = options\r\n\r\n return editor\r\n .chain()\r\n .focus()\r\n .command(({ state, tr, dispatch }) => {\r\n const selection = state.selection as unknown as SelectionWithCells\r\n const cellsToUpdate: number[] = []\r\n\r\n // 1. Gather all targeted cell positions\r\n if (selection instanceof CellSelection || selection.forEachCell) {\r\n selection.forEachCell!((_node, pos) => {\r\n cellsToUpdate.push(pos)\r\n })\r\n } else {\r\n const $pos = selection.$anchor\r\n for (let d = $pos.depth; d > 0; d--) {\r\n const nodeName = $pos.node(d).type.name\r\n if (nodeName === 'tableCell' || nodeName === 'tableHeader') {\r\n cellsToUpdate.push($pos.before(d))\r\n break\r\n }\r\n }\r\n }\r\n\r\n if (cellsToUpdate.length === 0) return false\r\n\r\n if (dispatch) {\r\n cellsToUpdate.forEach((cellPos) => {\r\n const cellNode = tr.doc.nodeAt(cellPos)\r\n if (!cellNode) return\r\n\r\n // Clone existing node attributes to keep colors or spans intact\r\n const updatedAttrs = { ...cellNode.attrs }\r\n\r\n // 2. Set horizontal property safely\r\n if (horizontal) {\r\n updatedAttrs.alignment = horizontal\r\n }\r\n\r\n // 3. Set vertical property safely\r\n if (vertical) {\r\n updatedAttrs.verticalAlignment = vertical\r\n }\r\n\r\n // 4. Update the node markup structural properties in the transaction\r\n tr.setNodeMarkup(cellPos, undefined, updatedAttrs)\r\n })\r\n\r\n dispatch(tr)\r\n return true\r\n }\r\n\r\n return true\r\n })\r\n .run()\r\n}\r\n\r\n\r\n/**\r\n * Sorts table rows based on the text properties of the active column.\r\n * Skips the first row if it consists purely of tableHeaders.\r\n */\r\nexport const sortActiveColumn = (editor: Editor, order: 'asc' | 'desc'): boolean => {\r\n return editor\r\n .chain()\r\n .focus()\r\n .command(({ state, dispatch }) => {\r\n const context = getTableContext(state)\r\n if (!context || !dispatch) return false\r\n\r\n const { tableNode, tablePos, map, cellRect } = context\r\n const currentColumnIndex = cellRect.left\r\n const tr = state.tr\r\n\r\n const rowsData: { rowNode: any; textContent: string }[] = []\r\n let startRowIndex = 0\r\n\r\n // 1. Detect if the first row is a header row to avoid sorting it\r\n const firstRowCells = tableNode.child(0)\r\n const isFirstRowHeader = firstRowCells.child(0).type.name === 'tableHeader'\r\n if (isFirstRowHeader) {\r\n startRowIndex = 1\r\n }\r\n\r\n // 2. Extract and match text data arrays from rows\r\n for (let r = startRowIndex; r < map.height; r++) {\r\n const rowNode = tableNode.child(r)\r\n const mapIndex = r * map.width + currentColumnIndex\r\n const cellLocalPos = map.map[mapIndex]\r\n const cellNode = tableNode.nodeAt(cellLocalPos)\r\n\r\n rowsData.push({\r\n rowNode,\r\n textContent: cellNode ? cellNode.textContent.trim().toLowerCase() : '',\r\n })\r\n }\r\n\r\n // 3. Perform sorting comparison using native JavaScript collators\r\n const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })\r\n rowsData.sort((a, b) => {\r\n const comparison = collator.compare(a.textContent, b.textContent)\r\n return order === 'asc' ? comparison : -comparison\r\n })\r\n\r\n // 4. Reconstruct row layouts mapping nodes internally\r\n const updatedRows: any[] = []\r\n if (isFirstRowHeader) {\r\n updatedRows.push(tableNode.child(0)) // Keep header at the top\r\n }\r\n rowsData.forEach((item) => updatedRows.push(item.rowNode))\r\n\r\n // 5. Overwrite the table's structural contents inside the transaction safely\r\n const tableContentStart = tablePos + 1\r\n const tableContentEnd = tablePos + tableNode.nodeSize - 1\r\n\r\n const newTableContent = Fragment.from(updatedRows)\r\n tr.replaceWith(tableContentStart, tableContentEnd, newTableContent)\r\n\r\n dispatch(tr)\r\n return true\r\n })\r\n .run()\r\n}\r\n","import { Editor } from \"@tiptap/core\";\r\nimport { getTableContext } from \"./getTableContext\";\r\n\r\ntype HorizontalAlign = \"left\" | \"center\" | \"right\" | \"justify\";\r\ntype VerticalAlign = \"top\" | \"middle\" | \"bottom\";\r\n\r\ninterface TableAlignmentState {\r\n isInsideTable: boolean;\r\n horizontal: HorizontalAlign;\r\n vertical: VerticalAlign;\r\n}\r\n\r\n/**\r\n * Extracts the active alignment values from the currently selected or focused table cell.\r\n * Returns defaults if the selection is outside a table structure.\r\n */\r\nexport const getActiveTableCellAlignment = (\r\n editor: Editor | null,\r\n): TableAlignmentState => {\r\n const defaultState: TableAlignmentState = {\r\n isInsideTable: false,\r\n horizontal: \"left\",\r\n vertical: \"top\",\r\n };\r\n\r\n if (!editor) return defaultState;\r\n\r\n // Use the shared context helper to safely extract current cell node data\r\n const context = getTableContext(editor.state);\r\n if (!context) return defaultState;\r\n\r\n const { tableNode, map, cellPos } = context;\r\n\r\n // Find the cell node at the active coordinate within the transaction document\r\n const cellNode = editor.state.doc.nodeAt(cellPos);\r\n if (!cellNode) return defaultState;\r\n\r\n return {\r\n isInsideTable: true,\r\n // Safely read the attributes with custom fallbacks if undefined\r\n horizontal: (cellNode.attrs.alignment as HorizontalAlign) || \"left\",\r\n vertical: (cellNode.attrs.verticalAlignment as VerticalAlign) || \"top\",\r\n };\r\n};\r\n","/**\n * Từ điển chuỗi hiển thị của editor.\n *\n * Không dùng thư viện i18n nào: package chỉ có ~90 chuỗi tĩnh, không số nhiều,\n * không nội suy phức tạp. Thêm một runtime i18n vào đây chỉ tổ bắt app chủ phải\n * hoà giải hai hệ thống dịch.\n *\n * `vi` là nguồn chuẩn — kiểu `Messages` suy ra từ nó, nên thiếu một khoá ở `en`\n * là lỗi biên dịch chứ không phải chuỗi rỗng lúc chạy.\n */\nexport const vi = {\n\tcommon: {\n\t\tclose: \"Đóng\",\n\t\tdelete: \"Xoá\",\n\t\tcolor: \"Màu\",\n\t\trecentlyUsed: \"Dùng gần đây\",\n\t\tnoResults: \"Không tìm thấy kết quả\",\n\t\tmoreOptions: \"Tuỳ chọn khác\",\n\t},\n\n\t/** Nhãn căn chỉnh dùng chung cho chữ, ảnh và ô bảng. */\n\talign: {\n\t\tleft: \"Căn trái\",\n\t\tcenter: \"Căn giữa\",\n\t\tright: \"Căn phải\",\n\t\tjustify: \"Căn đều\",\n\t\ttop: \"Căn trên\",\n\t\tmiddle: \"Căn giữa theo chiều dọc\",\n\t\tbottom: \"Căn dưới\",\n\t},\n\n\t/** Nhãn định dạng chữ trên bubble menu. */\n\tmarks: {\n\t\tbold: \"In đậm\",\n\t\titalic: \"In nghiêng\",\n\t\tunderline: \"Gạch chân\",\n\t\tstrike: \"Gạch ngang\",\n\t\tcode: \"Mã\",\n\t\tlink: \"Liên kết\",\n\t},\n\n\tplaceholder: {\n\t\temptyDocument: \"Nhập nội dung, hoặc gõ '/' để mở danh sách lệnh…\",\n\t\theading: (level: number) => `Tiêu đề ${level}`,\n\t},\n\n\tslash: {\n\t\tfilterTag: \"Bộ lọc\",\n\t\tfilterHint: \"Bộ lọc…\",\n\t\tgroups: {\n\t\t\tstyle: \"Định dạng\",\n\t\t\tinsert: \"Chèn\",\n\t\t\tupload: \"Tải lên\",\n\t\t},\n\t},\n\n\tblocks: {\n\t\ttext: { title: \"Văn bản\", description: \"Bắt đầu viết với văn bản thường.\" },\n\t\theading1: { title: \"Tiêu đề 1\", description: \"Tiêu đề lớn.\" },\n\t\theading2: { title: \"Tiêu đề 2\", description: \"Tiêu đề vừa.\" },\n\t\theading3: { title: \"Tiêu đề 3\", description: \"Tiêu đề nhỏ.\" },\n\t\tbulletList: { title: \"Danh sách gạch đầu dòng\", description: \"Danh sách không đánh số.\" },\n\t\torderedList: { title: \"Danh sách đánh số\", description: \"Danh sách có đánh số thứ tự.\" },\n\t\ttaskList: { title: \"Danh sách công việc\", description: \"Danh sách có ô đánh dấu hoàn thành.\" },\n\t\tblockquote: { title: \"Trích dẫn\", description: \"Chèn một khối trích dẫn.\" },\n\t\tcodeBlock: { title: \"Khối mã\", description: \"Chèn một khối mã nguồn.\" },\n\t\thardBreak: { title: \"Ngắt dòng\", description: \"Xuống dòng trong cùng một đoạn.\" },\n\t\tclearLine: { title: \"Xoá dòng\", description: \"Xoá sạch nội dung dòng hiện tại.\" },\n\t\tseparator: { title: \"Đường kẻ ngang\", description: \"Chèn một đường phân cách.\" },\n\t\temoji: { title: \"Emoji\", description: \"Chèn biểu tượng cảm xúc.\" },\n\t\tmention: { title: \"Nhắc đến\", description: \"Nhắc đến một người.\" },\n\t\ttable: { title: \"Bảng\", description: \"Chèn một bảng.\" },\n\t\ttableOfContents: { title: \"Mục lục\", description: \"Chèn mục lục tự cập nhật theo các tiêu đề.\" },\n\t\timage: { title: \"Hình ảnh\", description: \"Chèn vùng tải ảnh lên.\" },\n\t\tattachment: { title: \"Tệp đính kèm\", description: \"Tải lên Word, PDF, bảng tính…\" },\n\t},\n\n\tbubble: {\n\t\tturnInto: \"Chuyển thành\",\n\t\tlinkPlaceholder: \"Dán liên kết…\",\n\t\timprove: \"Cải thiện\",\n\t\tpasteLink: \"Dán liên kết…\",\n\t\ttextColor: \"Màu chữ\",\n\t\tbackgroundColor: \"Màu nền\",\n\t\tincreaseIndent: \"Tăng thụt lề\",\n\t\tdecreaseIndent: \"Giảm thụt lề\",\n\t},\n\n\tdrag: {\n\t\thandleHint: \"Nhấn để mở tuỳ chọn, giữ để kéo\",\n\t\tresetFormatting: \"Xoá định dạng\",\n\t\tduplicate: \"Nhân đôi\",\n\t\tcopyToClipboard: \"Sao chép\",\n\t\tcopyAnchorLink: \"Sao chép liên kết neo\",\n\t\taskAi: \"Hỏi AI\",\n\t\tinsertBlock: \"Chèn khối\",\n\t},\n\n\ttable: {\n\t\talignment: \"Căn chỉnh\",\n\t\talignLeft: \"Căn trái\",\n\t\talignCenter: \"Căn giữa\",\n\t\talignRight: \"Căn phải\",\n\t\talignJustify: \"Căn đều\",\n\t\talignTop: \"Căn trên\",\n\t\talignMiddle: \"Căn giữa theo chiều dọc\",\n\t\talignBottom: \"Căn dưới\",\n\t\tmergeCells: \"Gộp ô\",\n\t\tsplitCell: \"Tách ô\",\n\t\tinsertRowBefore: \"Chèn hàng phía trên\",\n\t\tinsertRowAfter: \"Chèn hàng phía dưới\",\n\t\tinsertColumnBefore: \"Chèn cột bên trái\",\n\t\tinsertColumnAfter: \"Chèn cột bên phải\",\n\t\tdeleteRow: \"Xoá hàng\",\n\t\tdeleteColumn: \"Xoá cột\",\n\t\tduplicateRow: \"Nhân đôi hàng\",\n\t\tduplicateColumn: \"Nhân đôi cột\",\n\t\tclearRowContents: \"Xoá nội dung hàng\",\n\t\tclearColumnContents: \"Xoá nội dung cột\",\n\t\tmoveRowUp: \"Chuyển hàng lên\",\n\t\tmoveRowDown: \"Chuyển hàng xuống\",\n\t\tmoveColumnLeft: \"Chuyển cột sang trái\",\n\t\tmoveColumnRight: \"Chuyển cột sang phải\",\n\t\tdeleteTable: \"Xoá bảng\",\n\t},\n\n\timage: {\n\t\tclickToUpload: \"Nhấn để tải ảnh lên\",\n\t\tdelete: \"Xoá ảnh\",\n\t\torDragAndDrop: \"hoặc kéo thả vào đây\",\n\t\tlimitHint: (limit: number, maxSizeMb: number) =>\n\t\t\t`Tối đa ${limit} tệp, mỗi tệp ${maxSizeMb}MB.`,\n\t\tclearAll: \"Xoá tất cả\",\n\t\tcaption: \"Chú thích\",\n\t\tdownload: \"Tải ảnh về\",\n\t\treplace: \"Thay ảnh\",\n\t},\n\n\temojiPicker: {\n\t\tempty: \"Không tìm thấy emoji\",\n\t\tloading: \"Đang tải…\",\n\t},\n\n\tattachment: {\n\t\tclickToUpload: \"Nhấn để chọn tệp (Word, PDF, bảng tính…)\",\n\t\tuploading: \"Đang tải tệp lên…\",\n\t\tdownload: \"Tải tệp về\",\n\t},\n\n\ttoc: {\n\t\ttitle: \"Mục lục\",\n\t\tempty: \"Chưa có tiêu đề nào\",\n\t},\n};\n\n/** Hình dạng từ điển, suy ra từ bản tiếng Việt. */\nexport type Messages = typeof vi;\n\nexport const en: Messages = {\n\tcommon: {\n\t\tclose: \"Close\",\n\t\tdelete: \"Delete\",\n\t\tcolor: \"Color\",\n\t\trecentlyUsed: \"Recently used\",\n\t\tnoResults: \"No results found\",\n\t\tmoreOptions: \"More options\",\n\t},\n\n\talign: {\n\t\tleft: \"Align left\",\n\t\tcenter: \"Align center\",\n\t\tright: \"Align right\",\n\t\tjustify: \"Justify\",\n\t\ttop: \"Align top\",\n\t\tmiddle: \"Align middle\",\n\t\tbottom: \"Align bottom\",\n\t},\n\n\tmarks: {\n\t\tbold: \"Bold\",\n\t\titalic: \"Italic\",\n\t\tunderline: \"Underline\",\n\t\tstrike: \"Strikethrough\",\n\t\tcode: \"Code\",\n\t\tlink: \"Link\",\n\t},\n\n\tplaceholder: {\n\t\temptyDocument: \"Write, or type '/' for commands…\",\n\t\theading: (level: number) => `Heading ${level}`,\n\t},\n\n\tslash: {\n\t\tfilterTag: \"Filters\",\n\t\tfilterHint: \"Filters…\",\n\t\tgroups: {\n\t\t\tstyle: \"Style\",\n\t\t\tinsert: \"Insert\",\n\t\t\tupload: \"Upload\",\n\t\t},\n\t},\n\n\tblocks: {\n\t\ttext: { title: \"Text\", description: \"Just start typing with plain text.\" },\n\t\theading1: { title: \"Heading 1\", description: \"Big section heading.\" },\n\t\theading2: { title: \"Heading 2\", description: \"Medium section heading.\" },\n\t\theading3: { title: \"Heading 3\", description: \"Small section heading.\" },\n\t\tbulletList: { title: \"Bullet list\", description: \"An unordered list.\" },\n\t\torderedList: { title: \"Numbered list\", description: \"A list with numbering.\" },\n\t\ttaskList: { title: \"To-do list\", description: \"A list with checkboxes.\" },\n\t\tblockquote: { title: \"Blockquote\", description: \"Insert a quote block.\" },\n\t\tcodeBlock: { title: \"Code block\", description: \"Insert a block of code.\" },\n\t\thardBreak: { title: \"Line break\", description: \"Break a line inside a paragraph.\" },\n\t\tclearLine: { title: \"Clear line\", description: \"Clear the current line.\" },\n\t\tseparator: { title: \"Divider\", description: \"Insert a horizontal rule.\" },\n\t\temoji: { title: \"Emoji\", description: \"Insert an emoji.\" },\n\t\tmention: { title: \"Mention\", description: \"Mention a person.\" },\n\t\ttable: { title: \"Table\", description: \"Insert a table.\" },\n\t\ttableOfContents: { title: \"Table of contents\", description: \"Insert a live outline of your headings.\" },\n\t\timage: { title: \"Image\", description: \"Insert an image upload zone.\" },\n\t\tattachment: { title: \"Attachment\", description: \"Upload a Word, PDF or spreadsheet file.\" },\n\t},\n\n\tbubble: {\n\t\tturnInto: \"Turn into\",\n\t\tlinkPlaceholder: \"Paste a link…\",\n\t\timprove: \"Improve\",\n\t\tpasteLink: \"Paste a link…\",\n\t\ttextColor: \"Text color\",\n\t\tbackgroundColor: \"Background color\",\n\t\tincreaseIndent: \"Increase indent\",\n\t\tdecreaseIndent: \"Decrease indent\",\n\t},\n\n\tdrag: {\n\t\thandleHint: \"Click for options, hold to drag\",\n\t\tresetFormatting: \"Reset formatting\",\n\t\tduplicate: \"Duplicate\",\n\t\tcopyToClipboard: \"Copy to clipboard\",\n\t\tcopyAnchorLink: \"Copy anchor link\",\n\t\taskAi: \"Ask AI\",\n\t\tinsertBlock: \"Insert block\",\n\t},\n\n\ttable: {\n\t\talignment: \"Alignment\",\n\t\talignLeft: \"Align left\",\n\t\talignCenter: \"Align center\",\n\t\talignRight: \"Align right\",\n\t\talignJustify: \"Justify\",\n\t\talignTop: \"Align top\",\n\t\talignMiddle: \"Align middle\",\n\t\talignBottom: \"Align bottom\",\n\t\tmergeCells: \"Merge cells\",\n\t\tsplitCell: \"Split cell\",\n\t\tinsertRowBefore: \"Insert row above\",\n\t\tinsertRowAfter: \"Insert row below\",\n\t\tinsertColumnBefore: \"Insert column left\",\n\t\tinsertColumnAfter: \"Insert column right\",\n\t\tdeleteRow: \"Delete row\",\n\t\tdeleteColumn: \"Delete column\",\n\t\tduplicateRow: \"Duplicate row\",\n\t\tduplicateColumn: \"Duplicate column\",\n\t\tclearRowContents: \"Clear row contents\",\n\t\tclearColumnContents: \"Clear column contents\",\n\t\tmoveRowUp: \"Move row up\",\n\t\tmoveRowDown: \"Move row down\",\n\t\tmoveColumnLeft: \"Move column left\",\n\t\tmoveColumnRight: \"Move column right\",\n\t\tdeleteTable: \"Delete table\",\n\t},\n\n\timage: {\n\t\tclickToUpload: \"Click to upload\",\n\t\tdelete: \"Delete image\",\n\t\torDragAndDrop: \"or drag and drop\",\n\t\tlimitHint: (limit: number, maxSizeMb: number) =>\n\t\t\t`Maximum ${limit} file${limit === 1 ? \"\" : \"s\"}, ${maxSizeMb}MB each.`,\n\t\tclearAll: \"Clear all\",\n\t\tcaption: \"Caption\",\n\t\tdownload: \"Download image\",\n\t\treplace: \"Replace\",\n\t},\n\n\temojiPicker: {\n\t\tempty: \"No emoji found\",\n\t\tloading: \"Loading…\",\n\t},\n\n\tattachment: {\n\t\tclickToUpload: \"Click to choose a file (Word, PDF, spreadsheet…)\",\n\t\tuploading: \"Uploading file…\",\n\t\tdownload: \"Download file\",\n\t},\n\n\ttoc: {\n\t\ttitle: \"Table of contents\",\n\t\tempty: \"No headings found\",\n\t},\n};\n\nexport type Locale = \"vi\" | \"en\";\n\n/** Tiếng Việt là mặc định. */\nexport const DEFAULT_LOCALE: Locale = \"vi\";\n\nconst dictionaries: Record<Locale, Messages> = { vi, en };\n\nexport const getMessages = (locale: Locale = DEFAULT_LOCALE): Messages =>\n\tdictionaries[locale] ?? dictionaries[DEFAULT_LOCALE];\n","import { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { DEFAULT_LOCALE, getMessages, type Locale, type Messages } from \"./messages\";\n\nconst MessagesContext = createContext<Messages>(getMessages(DEFAULT_LOCALE));\n\n/**\n * Cung cấp từ điển cho các component bên trong editor.\n *\n * Phần extension không đi qua context được — chúng được dựng ngoài cây React —\n * nên `extensions()` nhận `locale` riêng. Cả hai cùng đọc `getMessages` nên\n * không có nguy cơ lệch nhau.\n */\nexport const I18nProvider = ({\n\tlocale = DEFAULT_LOCALE,\n\tchildren,\n}: {\n\tlocale?: Locale;\n\tchildren: ReactNode;\n}) => {\n\tconst messages = useMemo(() => getMessages(locale), [locale]);\n\treturn (\n\t\t<MessagesContext.Provider value={messages}>\n\t\t\t{children}\n\t\t</MessagesContext.Provider>\n\t);\n};\n\n/** Từ điển đang dùng. Ngoài provider thì rơi về tiếng Việt. */\nexport const useMessages = (): Messages => useContext(MessagesContext);\n","import { Editor } from \"@tiptap/core\";\r\nimport {\r\n DropdownMenuPortal,\r\n DropdownMenuSeparator,\r\n DropdownMenuSub,\r\n DropdownMenuSubContent,\r\n DropdownMenuSubTrigger,\r\n} from \"~/components/ui/dropdown-menu\";\r\nimport { TableMenuItem } from \"./TableMenuItem\";\r\nimport {\r\n AlignVerticalJustifyCenter,\r\n AlignVerticalJustifyEnd,\r\n AlignVerticalJustifyStart,\r\n TextAlignCenter,\r\n TextAlignEnd,\r\n TextAlignStart,\r\n} from \"lucide-react\";\r\nimport { AlignmentIcon } from \"~/components/color/icon\";\r\nimport { ITEM_CLASSNAME } from \"./TableMenuPopover\";\r\nimport { setTableCellAlignment } from \"../utils/table\";\r\nimport { getActiveTableCellAlignment } from \"../utils/getActiveTableCellAlignment\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\nexport const AlignmentDropdown = ({ editor }: { editor: Editor }) => {\r\n const t = useMessages();\r\n const { horizontal, vertical } = getActiveTableCellAlignment(editor);\r\n\r\n\r\n return (\r\n <DropdownMenuSub>\r\n <DropdownMenuSubTrigger className={ITEM_CLASSNAME}>\r\n <AlignmentIcon className=\"size-4 shrink-0 text-muted-foreground\" />\r\n <span>{t.table.alignment}</span>\r\n </DropdownMenuSubTrigger>\r\n <DropdownMenuPortal>\r\n <DropdownMenuSubContent\r\n sideOffset={8}\r\n alignOffset={-90}\r\n className=\"w-50 z-120\"\r\n >\r\n <TableMenuItem\r\n onClick={() =>\r\n setTableCellAlignment(editor, {\r\n horizontal: \"left\",\r\n })\r\n }\r\n icon={TextAlignStart}\r\n label={t.table.alignLeft}\r\n className={\r\n horizontal === \"left\" ? \"text-foreground! bg-accent!\" : \"\"\r\n }\r\n />\r\n <TableMenuItem\r\n icon={TextAlignCenter}\r\n label={t.table.alignCenter}\r\n onClick={() =>\r\n setTableCellAlignment(editor, {\r\n horizontal: \"center\",\r\n })\r\n }\r\n className={\r\n horizontal === \"center\" ? \"text-foreground! bg-accent!\" : \"\"\r\n }\r\n />\r\n <TableMenuItem\r\n icon={TextAlignEnd}\r\n label={t.table.alignRight}\r\n onClick={() =>\r\n setTableCellAlignment(editor, {\r\n horizontal: \"right\",\r\n })\r\n }\r\n className={\r\n horizontal === \"right\" ? \"text-foreground! bg-accent!\" : \"\"\r\n }\r\n />\r\n\r\n <DropdownMenuSeparator />\r\n\r\n <TableMenuItem\r\n icon={AlignVerticalJustifyStart}\r\n label={t.table.alignTop}\r\n onClick={() =>\r\n setTableCellAlignment(editor, {\r\n vertical: \"top\",\r\n })\r\n }\r\n className={vertical === \"top\" ? \"text-foreground! bg-accent!\" : \"\"}\r\n />\r\n <TableMenuItem\r\n icon={AlignVerticalJustifyCenter}\r\n label={t.table.alignMiddle}\r\n onClick={() =>\r\n setTableCellAlignment(editor, {\r\n vertical: \"middle\",\r\n })\r\n }\r\n className={\r\n vertical === \"middle\" ? \"text-foreground! bg-accent!\" : \"\"\r\n }\r\n />\r\n <TableMenuItem\r\n icon={AlignVerticalJustifyEnd}\r\n label={t.table.alignBottom}\r\n onClick={() =>\r\n setTableCellAlignment(editor, {\r\n vertical: \"bottom\",\r\n })\r\n }\r\n className={\r\n vertical === \"bottom\" ? \"text-foreground! bg-accent!\" : \"\"\r\n }\r\n />\r\n </DropdownMenuSubContent>\r\n </DropdownMenuPortal>\r\n </DropdownMenuSub>\r\n );\r\n};\r\n","import { CSSProperties } from \"react\";\r\nimport { DropdownMenuItem } from \"../ui/dropdown-menu\";\r\nimport { Color } from \"./type\";\r\nimport { TextIcon } from \"./icon\";\r\n\r\nexport const ColorItem = ({\r\n color,\r\n onClick,\r\n}: {\r\n color: Color;\r\n onClick: () => void;\r\n}) => {\r\n return (\r\n <DropdownMenuItem\r\n onClick={onClick}\r\n className=\"py-1.5 rounded-lg text-muted-foreground px-2.5 gap-2\"\r\n >\r\n {color.type === \"text\" ? (\r\n <span\r\n className=\"size-5 flex items-center justify-center rounded-full tiptap-button-color-text\"\r\n style={\r\n {\r\n color: `var(--tt-color-text-${color.value})`,\r\n \"--color-text-button-color\": `var(--tt-color-text-${color.value})`,\r\n } as CSSProperties\r\n }\r\n >\r\n <TextIcon />\r\n </span>\r\n ) : (\r\n <span\r\n className=\"size-5 flex items-center justify-center rounded-full tiptap-button-highlight\"\r\n style={\r\n {\r\n \"--highlight-color\":\r\n color.value === \"default\"\r\n ? \"var(--background)\"\r\n : `var(--tt-color-highlight-${color.value})`,\r\n } as CSSProperties\r\n }\r\n ></span>\r\n )}\r\n {color.name} {color.type}\r\n </DropdownMenuItem>\r\n );\r\n};\r\n","import { Color, GroupColors } from \"./type\";\r\nimport type { Messages } from \"~/i18n\";\r\n\r\nexport const RECENT_COLORS_KEY = \"recentlyUsedColors\";\r\nexport const MAX_RECENT = 3;\r\n\r\nexport const colors: Color[] = [\r\n {\r\n name: \"Default\",\r\n value: \"default\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Gray\",\r\n value: \"gray\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Brown\",\r\n value: \"brown\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Orange\",\r\n value: \"orange\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Yellow\",\r\n value: \"yellow\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Green\",\r\n value: \"green\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Blue\",\r\n value: \"blue\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Purple\",\r\n value: \"purple\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Pink\",\r\n value: \"pink\",\r\n type: \"\",\r\n },\r\n {\r\n name: \"Red\",\r\n value: \"red\",\r\n type: \"\",\r\n },\r\n];\r\n\r\n/**\r\n * Nhóm màu, dựng theo ngôn ngữ đang dùng.\r\n *\r\n * Là hàm chứ không phải hằng vì nhãn phải dịch được, mà module cấp cao nhất\r\n * thì không gọi hook được.\r\n */\r\nexport const createGroupColors = (t: Messages): GroupColors[] => [\r\n {\r\n title: t.bubble.textColor,\r\n colors: colors.map((color) => ({ ...color, type: \"text\" })),\r\n },\r\n {\r\n title: t.bubble.backgroundColor,\r\n colors: colors.map((color) => ({ ...color, type: \"background\" })),\r\n },\r\n];\r\n","import { Dispatch, SetStateAction } from \"react\";\nimport { createGroupColors, MAX_RECENT, RECENT_COLORS_KEY } from \"./constant\";\nimport type { Messages } from \"~/i18n\";\nimport { Color, GroupColors } from \"./type\";\n\nexport const getRecentColors = (): Color[] => {\n if (typeof window === \"undefined\") return [];\n const stored = localStorage.getItem(RECENT_COLORS_KEY);\n return stored ? JSON.parse(stored) : [];\n};\n\n\nexport const handleAddRecentUsed = (\n color: Color,\n setItems: Dispatch<SetStateAction<GroupColors[]>>,\n t: Messages,\n) => {\n const currentRecent = getRecentColors();\n\n const filtered = currentRecent.filter(\n (c) => c.value !== color.value || c.type !== color.type,\n );\n\n const updatedRecent = [color, ...filtered].slice(0, MAX_RECENT);\n localStorage.setItem(RECENT_COLORS_KEY, JSON.stringify(updatedRecent));\n\n setItems(() => {\n const recentItem = { title: t.common.recentlyUsed, colors: updatedRecent };\n return [recentItem, ...createGroupColors(t)];\n });\n};\n","import { Editor } from \"@tiptap/core\";\nimport { PaintBucket } from \"lucide-react\";\nimport { useState } from \"react\";\n\nimport {\n DropdownMenuGroup,\n DropdownMenuLabel,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n} from \"~/components/ui/dropdown-menu\";\nimport { ScrollArea } from \"~/components/ui/scroll-area\";\nimport { ITEM_CLASSNAME } from \"./TableMenuPopover\";\nimport { setTableCellStyles } from \"../utils/table\";\nimport { ColorItem } from \"~/components/color/ColorItem\";\nimport { getRecentColors, handleAddRecentUsed } from \"~/components/color/utils\";\nimport { GroupColors } from \"~/components/color/type\";\nimport { createGroupColors } from \"~/components/color/constant\";\nimport { useMessages } from \"~/i18n\";\n\n\n\nexport const ColorsDropdwon = ({ editor }: { editor: Editor | null }) => {\n const t = useMessages();\n if (!editor) return;\n\n const [items, setItems] = useState<GroupColors[]>(() => {\n const recent = getRecentColors();\n if (recent.length > 0) {\n return [{ title: t.common.recentlyUsed, colors: recent }, ...createGroupColors(t)];\n }\n return createGroupColors(t);\n });\n\n return (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className={ITEM_CLASSNAME}>\n <PaintBucket className=\"size-4 shrink-0 text-muted-foreground\" />\n <span>{t.common.color}</span>\n </DropdownMenuSubTrigger>\n\n <DropdownMenuPortal>\n <DropdownMenuSubContent sideOffset={8} className=\"w-55 pr-px z-120\">\n <ScrollArea className=\"h-90 p-0.5 pr-3\">\n {items.map((item) => (\n <DropdownMenuGroup key={item.title}>\n <DropdownMenuLabel className=\"text-foreground\">\n {item.title}\n </DropdownMenuLabel>\n {item.colors.map((color) => (\n <ColorItem\n onClick={() => {\n // setTableCellStyles(editor, {\n // backgroundColor: \"#3b82f6\", // Tailwind Blue 500\n // textColor: \"#ffffff\", // White Text\n // });\n handleAddRecentUsed(color, setItems, t);\n\n if (color.type === \"background\") {\n setTableCellStyles(editor, {\n backgroundColor: `var(--tt-color-text-${color.value})`,\n });\n return;\n }\n\n setTableCellStyles(editor, {\n textColor: `var(--tt-color-text-${color.value})`,\n });\n }}\n key={color.value}\n color={color}\n />\n ))}\n </DropdownMenuGroup>\n ))}\n </ScrollArea>\n </DropdownMenuSubContent>\n </DropdownMenuPortal>\n </DropdownMenuSub>\n );\n};\n","import { Editor } from '@tiptap/core'\r\nimport { TableMap } from '@tiptap/pm/tables'\r\nimport { ResolvedPos } from '@tiptap/pm/model'\r\n\r\ninterface CellSelection extends Selection {\r\n $anchor: any\r\n anchorCell?: ResolvedPos\r\n}\r\n\r\ninterface TableMatrixMeta {\r\n isInsideTable: boolean\r\n // Column states\r\n currentColumnIndex: number\r\n isFirstColumn: boolean\r\n isLastColumn: boolean\r\n // Row states\r\n currentRowIndex: number\r\n isFirstRow: boolean\r\n isLastRow: boolean\r\n}\r\n/**\r\n * Calculates whether the current column is at the boundaries of the table.\r\n */\r\nexport const getTableColumnMeta = (editor: Editor | null): TableMatrixMeta => {\r\n const defaultMeta: TableMatrixMeta = {\r\n isInsideTable: false,\r\n currentColumnIndex: -1,\r\n isFirstColumn: true,\r\n isLastColumn: true,\r\n currentRowIndex: -1,\r\n isFirstRow: true,\r\n isLastRow: true\r\n }\r\n\r\n if (!editor) return defaultMeta\r\n\r\n const { state } = editor\r\n const selection = state.selection as unknown as CellSelection\r\n let cellPos: number | null = null\r\n\r\n // 1. Identify selected cell position\r\n if (selection.anchorCell) {\r\n cellPos = selection.anchorCell.pos\r\n } else {\r\n const $pos = selection.$anchor\r\n for (let d = $pos.depth; d > 0; d--) {\r\n const nodeName = $pos.node(d).type.name\r\n if (nodeName === 'tableCell' || nodeName === 'tableHeader') {\r\n cellPos = $pos.before(d)\r\n break\r\n }\r\n }\r\n }\r\n\r\n\r\n\r\n if (cellPos === null) return defaultMeta\r\n\r\n // 2. Locate parent table node\r\n const $cellPos = state.doc.resolve(cellPos)\r\n let tableNode = null\r\n let tablePos = -1\r\n\r\n for (let d = $cellPos.depth; d > 0; d--) {\r\n if ($cellPos.node(d).type.name === 'table') {\r\n tableNode = $cellPos.node(d)\r\n tablePos = $cellPos.before(d)\r\n break\r\n }\r\n }\r\n\r\n if (!tableNode || tablePos === -1) return defaultMeta\r\n\r\n // 3. Map out table structural dimensions\r\n const map = TableMap.get(tableNode)\r\n const localCellPos = cellPos - tablePos - 1\r\n\r\n\r\n // 4. Extract boundaries rect using findCell mapping\r\n // findCell returns: { top: number, bottom: number, left: number, right: number }\r\n const cellRect = map.findCell(localCellPos)\r\n\r\n // Left property yields the exact 0-indexed column position\r\n const currentColumnIndex = cellRect.left\r\n\r\n // Top property yields the exact 0-indexed row position\r\n const currentRowIndex = cellRect.top\r\n\r\n // 5. Compute structural boundary metrics\r\n return {\r\n isInsideTable: true,\r\n currentColumnIndex,\r\n isFirstColumn: currentColumnIndex === 0,\r\n isLastColumn: currentColumnIndex === map.width - 1,\r\n currentRowIndex,\r\n isFirstRow: currentRowIndex === 0,\r\n // map.height represents total table rows count\r\n isLastRow: currentRowIndex === map.height - 1,\r\n }\r\n}\r\n","import { Editor } from '@tiptap/core'\r\nimport { getTableContext } from './getTableContext'\r\n\r\n/**\r\n * Checks if any cell in the currently active column contains text content.\r\n * Used to enable/disable A-Z and Z-A sorting actions.\r\n */\r\nexport const isCurrentColumnSortable = (editor: Editor | null): boolean => {\r\n if (!editor) return false\r\n\r\n const context = getTableContext(editor.state)\r\n if (!context) return false\r\n\r\n const { tableNode, map, cellRect } = context\r\n const currentColumnIndex = cellRect.left\r\n\r\n for (let r = 0; r < map.height; r++) {\r\n const mapIndex = r * map.width + currentColumnIndex\r\n const cellLocalPos = map.map[mapIndex]\r\n\r\n const cellNode = tableNode.nodeAt(cellLocalPos)\r\n if (cellNode && cellNode.textContent.trim().length > 0) {\r\n return true // At least one cell contains sorting text data\r\n }\r\n }\r\n\r\n return false\r\n}\r\n\r\ntype TableDimension = 'column' | 'row'\r\n\r\n/**\r\n * Checks if there is any text available inside the active column or row.\r\n * Returns true if content is found, allowing you to enable \"Clear Content\" buttons.\r\n * \r\n * @param editor The active Tiptap Editor instance.\r\n * @param dimension The structure to check ('column' | 'row').\r\n * @returns boolean\r\n */\r\nexport const hasTableDimensionContent = (\r\n editor: Editor | null,\r\n dimension: TableDimension\r\n): boolean => {\r\n if (!editor) return false\r\n\r\n const context = getTableContext(editor.state)\r\n if (!context) return false\r\n\r\n const { tableNode, map, cellRect } = context\r\n\r\n // 1. Check vertical Column structure\r\n if (dimension === 'column') {\r\n const currentColumnIndex = cellRect.left\r\n\r\n for (let r = 0; r < map.height; r++) {\r\n const mapIndex = r * map.width + currentColumnIndex\r\n const cellLocalPos = map.map[mapIndex]\r\n const cellNode = tableNode.nodeAt(cellLocalPos)\r\n\r\n // If a cell exists and has stripped string characters, content is available\r\n if (cellNode && cellNode.textContent.trim().length > 0) {\r\n return true\r\n }\r\n }\r\n }\r\n\r\n // 2. Check horizontal Row structure\r\n if (dimension === 'row') {\r\n const currentRowIndex = cellRect.top\r\n\r\n for (let c = 0; c < map.width; c++) {\r\n const mapIndex = currentRowIndex * map.width + c\r\n const cellLocalPos = map.map[mapIndex]\r\n const cellNode = tableNode.nodeAt(cellLocalPos)\r\n\r\n if (cellNode && cellNode.textContent.trim().length > 0) {\r\n return true\r\n }\r\n }\r\n }\r\n\r\n return false\r\n}\r\n","import { Editor } from \"@tiptap/core\";\r\nimport {\r\n ArrowDown,\r\n ArrowLeft,\r\n ArrowRight,\r\n ArrowUp,\r\n CopyIcon,\r\n EllipsisIcon,\r\n EllipsisVerticalIcon,\r\n SquareX,\r\n Trash,\r\n} from \"lucide-react\";\r\nimport { useState } from \"react\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuTrigger,\r\n DropdownMenuContent,\r\n DropdownMenuGroup,\r\n DropdownMenuSeparator,\r\n} from \"~/components/ui/dropdown-menu\";\r\nimport { cn } from \"~/lib/utils\";\r\nimport { ScrollArea } from \"~/components/ui/scroll-area\";\r\nimport {\r\n InsertLeftColumn,\r\n InsertRightColumn,\r\n InsertRowAbove,\r\n InsertRowBelow,\r\n} from \"~/components/icons\";\r\nimport { AlignmentDropdown } from \"./AlignmentContent\";\r\nimport { TableMenuItem } from \"./TableMenuItem\";\r\nimport { ColorsDropdwon } from \"./Colors\";\r\nimport { deleteCellSelection } from \"@tiptap/pm/tables\";\r\nimport { getTableColumnMeta } from \"../utils/getTableColumnMeta\";\r\n\r\nimport {\r\n duplicateActiveColumn,\r\n duplicateActiveRow,\r\n moveActiveColumn,\r\n moveActiveRow,\r\n} from \"../utils/table\";\r\nimport { hasTableDimensionContent } from \"../utils\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\nexport const ITEM_CLASSNAME =\r\n \"flex items-center gap-2 px-2.5 rounded-md text-sm font-medium transition-colors cursor-pointer w-full\";\r\n\r\ninterface TableMenuPopoverProps {\r\n editor: Editor;\r\n type: \"row\" | \"column\";\r\n pluginKey: any;\r\n}\r\n\r\nexport const TableMenuPopover = ({\r\n editor,\r\n type,\r\n pluginKey,\r\n}: TableMenuPopoverProps) => {\r\n const t = useMessages();\r\n const [opened, setOpened] = useState(false);\r\n const isRow = type === \"row\";\r\n\r\n const TriggerIcon = isRow ? EllipsisVerticalIcon : EllipsisIcon;\r\n const triggerStyles = isRow\r\n ? \"w-3 h-full rounded flex items-center justify-center transition-colors\"\r\n : \"w-full h-3 rounded flex items-center justify-center transition-colors\";\r\n\r\n const handleOpenChange = (op: boolean) => {\r\n setOpened(op);\r\n editor\r\n .chain()\r\n .command(({ tr }) => {\r\n tr.setMeta(pluginKey, { openedMenu: op });\r\n return true;\r\n })\r\n .run();\r\n };\r\n\r\n const { isInsideTable, isFirstColumn, isLastColumn, isFirstRow, isLastRow } =\r\n getTableColumnMeta(editor);\r\n\r\n const isRowClearable = hasTableDimensionContent(editor!, \"row\");\r\n const isColumnClearable = hasTableDimensionContent(editor!, \"column\");\r\n\r\n return (\r\n <DropdownMenu open={opened} onOpenChange={handleOpenChange}>\r\n <DropdownMenuTrigger\r\n className={cn(triggerStyles, {\r\n \"bg-primary text-primary-foreground\": opened,\r\n \"text-secondary-foreground bg-accent\": !opened,\r\n })}\r\n >\r\n <TriggerIcon className=\"size-4 shrink-0\" />\r\n </DropdownMenuTrigger>\r\n\r\n <DropdownMenuContent\r\n className=\"shadow-xl w-60 pr-px\"\r\n align=\"start\"\r\n side={isRow ? \"right\" : \"bottom\"}\r\n >\r\n <ScrollArea className=\"h-70 overflow-hidden overflow-y-auto p-0.5 pr-3\">\r\n {/* 1. Re-ordering Actions */}\r\n <DropdownMenuGroup>\r\n <TableMenuItem\r\n icon={isRow ? ArrowUp : ArrowLeft}\r\n label={isRow ? t.table.moveRowUp : t.table.moveColumnLeft}\r\n onClick={() => {\r\n isRow\r\n ? moveActiveRow(editor, \"up\")\r\n : moveActiveColumn(editor, \"left\");\r\n }}\r\n disabled={isRow ? isFirstRow : isFirstColumn}\r\n />\r\n <TableMenuItem\r\n icon={isRow ? ArrowDown : ArrowRight}\r\n label={isRow ? t.table.moveRowDown : t.table.moveColumnRight}\r\n onClick={() => {\r\n isRow\r\n ? moveActiveRow(editor, \"down\")\r\n : moveActiveColumn(editor, \"right\");\r\n }}\r\n disabled={isRow ? isLastRow : isLastColumn}\r\n />\r\n </DropdownMenuGroup>\r\n\r\n <DropdownMenuSeparator />\r\n\r\n {/* 2. Insertion Actions */}\r\n <DropdownMenuGroup>\r\n <TableMenuItem\r\n icon={isRow ? InsertRowAbove : InsertLeftColumn}\r\n label={isRow ? t.table.insertRowBefore : t.table.insertColumnBefore}\r\n onClick={() =>\r\n isRow\r\n ? editor.chain().focus().addRowBefore().run()\r\n : editor.chain().focus().addColumnBefore().run()\r\n }\r\n />\r\n <TableMenuItem\r\n icon={isRow ? InsertRowBelow : InsertRightColumn}\r\n label={isRow ? t.table.insertRowAfter : t.table.insertColumnAfter}\r\n onClick={() =>\r\n isRow\r\n ? editor.chain().focus().addRowAfter().run()\r\n : editor.chain().focus().addColumnAfter().run()\r\n }\r\n />\r\n </DropdownMenuGroup>\r\n\r\n <DropdownMenuSeparator />\r\n\r\n {/* 3. Sorting Actions */}\r\n {/* {isSortable && (\r\n <Fragment>\r\n <DropdownMenuGroup>\r\n <TableMenuItem\r\n icon={ArrowUpAZ}\r\n label={isRow ? \"Sort row A-Z\" : \"Sort column A-Z\"}\r\n onClick={() => {\r\n sortActiveColumn(editor!, \"asc\");\r\n }}\r\n />\r\n <TableMenuItem\r\n icon={ArrowDownZA}\r\n label={isRow ? \"Sort row Z-A\" : \"Sort column Z-A\"}\r\n onClick={() => {\r\n sortActiveColumn(editor!, \"desc\");\r\n }}\r\n />\r\n </DropdownMenuGroup>\r\n\r\n <DropdownMenuSeparator />\r\n </Fragment>\r\n )} */}\r\n\r\n <DropdownMenuGroup>\r\n <ColorsDropdwon editor={editor} />\r\n <AlignmentDropdown editor={editor} />\r\n {(isRowClearable || isColumnClearable) && (\r\n <TableMenuItem\r\n icon={SquareX}\r\n label={isRow ? t.table.clearRowContents : t.table.clearColumnContents}\r\n onClick={() =>\r\n editor\r\n .chain()\r\n .focus()\r\n .command(({ state, dispatch }) => {\r\n return deleteCellSelection(state, dispatch);\r\n })\r\n .run()\r\n }\r\n />\r\n )}\r\n </DropdownMenuGroup>\r\n\r\n {/* 5. Utility Content Modification Actions */}\r\n\r\n <DropdownMenuSeparator />\r\n\r\n <DropdownMenuGroup>\r\n <TableMenuItem\r\n icon={CopyIcon}\r\n label={isRow ? t.table.duplicateRow : t.table.duplicateColumn}\r\n onClick={() =>\r\n isRow\r\n ? duplicateActiveRow(editor)\r\n : duplicateActiveColumn(editor)\r\n }\r\n />\r\n\r\n {/* 6. Destructive Delete Actions */}\r\n <TableMenuItem\r\n icon={Trash}\r\n label={isRow ? t.table.deleteRow : t.table.deleteColumn}\r\n variant=\"destructive\"\r\n onClick={() =>\r\n isRow\r\n ? editor.chain().focus().deleteRow().run()\r\n : editor.chain().focus().deleteColumn().run()\r\n }\r\n />\r\n </DropdownMenuGroup>\r\n </ScrollArea>\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n );\r\n};\r\n","import { PaintBucket } from \"lucide-react\";\nimport { ITEM_CLASSNAME } from \"../node/table-node/popover/TableMenuPopover\";\nimport {\n DropdownMenuGroup,\n DropdownMenuLabel,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n} from \"../ui/dropdown-menu\";\nimport { ColorItem } from \"./ColorItem\";\nimport { ScrollArea } from \"../ui/scroll-area\";\nimport { Color, GroupColors } from \"./type\";\nimport { Editor } from \"@tiptap/core\";\nimport { getRecentColors } from \"./utils\";\nimport { useState } from \"react\";\nimport { createGroupColors } from \"./constant\";\nimport { useMessages } from \"~/i18n\";\n\nconst Colors = ({\n editor,\n setColor,\n}: {\n editor: Editor | null;\n setColor: (color: Color) => void;\n}) => {\n const t = useMessages();\n const [items, setItems] = useState<GroupColors[]>(() => {\n const recent = getRecentColors();\n if (recent.length > 0) {\n return [{ title: t.common.recentlyUsed, colors: recent }, ...createGroupColors(t)];\n }\n return createGroupColors(t);\n });\n\n return (\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className={ITEM_CLASSNAME}>\n <PaintBucket className=\"size-4 shrink-0 text-muted-foreground\" />\n <span>{t.common.color}</span>\n </DropdownMenuSubTrigger>\n\n <DropdownMenuPortal>\n <DropdownMenuSubContent sideOffset={8} className=\"w-55 pr-px z-120\">\n <ScrollArea className=\"h-90 p-0.5 pr-3\">\n {items.map((item) => (\n <DropdownMenuGroup key={item.title}>\n <DropdownMenuLabel className=\"text-foreground\">\n {item.title}\n </DropdownMenuLabel>\n {item.colors.map((color) => (\n <ColorItem\n onClick={() => setColor(color)}\n key={color.value}\n color={color}\n />\n ))}\n </DropdownMenuGroup>\n ))}\n </ScrollArea>\n </DropdownMenuSubContent>\n </DropdownMenuPortal>\n </DropdownMenuSub>\n );\n};\n\nexport default Colors;\n","import DragHandle from \"@tiptap/extension-drag-handle-react\";\nimport type { ComputePositionConfig } from \"@floating-ui/dom\";\nimport {\n Clipboard,\n Copy,\n GripVertical,\n Link,\n Plus,\n RotateCcw,\n Sparkles,\n Trash,\n} from \"lucide-react\";\nimport { Button } from \"../ui/button\";\nimport { Editor } from \"@tiptap/core\";\nimport TooltipWraper from \"../TooltipWrapper\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuTrigger,\n} from \"../ui/dropdown-menu\";\nimport { useState } from \"react\";\nimport {\n cn,\n copySelectedNodeToClipboard,\n deleteSelectedNode,\n duplicateSelectedNode,\n setUniversalBlockBackgroundColor,\n} from \"~/lib/utils\";\n\nimport { NodeSelection } from \"@tiptap/pm/state\";\n\nimport { Color } from \"../color/type\";\nimport Colors from \"../color/ColorSubMenu\";\nimport { handleAddRecentUsed } from \"../color/utils\";\nimport { useMessages } from \"~/i18n\";\n\n/**\n * Định danh phải ổn định giữa các lần render.\n *\n * DragHandle đưa object này thẳng vào deps của một useEffect, và mỗi lần deps\n * đổi nó gọi `editor.unregisterPlugin(...)`. ProseMirror phản ứng bằng cách\n * huỷ **toàn bộ** plugin view rồi dựng lại — kể cả plugin suggestion, nên\n * `onExit` chạy và menu slash bị gỡ khỏi DOM giữa chừng.\n *\n * Viết object literal thẳng trong JSX là tạo định danh mới mỗi lần render,\n * nên chỉ cần component cha re-render một lần là menu tắt.\n */\nconst DRAG_HANDLE_POSITION: ComputePositionConfig = {\n\tplacement: \"left-start\",\n\tstrategy: \"absolute\",\n};\n\nconst Dragable = ({ editor }: { editor: Editor | null }) => {\n const t = useMessages();\n const [isOpen, setIsOpen] = useState(false);\n const [currentNodeType, setCurrentNodeType] = useState<string | null>(null);\n\n if (!editor) {\n return;\n }\n\n const getNodePosFromHandle = (event: React.MouseEvent) => {\n const dragHandleEl = event.currentTarget.closest(\".drag-handle\");\n if (!dragHandleEl) return null;\n const rect = dragHandleEl.getBoundingClientRect();\n const xCoords = rect.right + 20;\n const yCoords = rect.top + rect.height / 2;\n return editor.view.posAtCoords({ left: xCoords, top: yCoords });\n };\n\n const handleInsertAtDragPosition = (\n event: React.MouseEvent<HTMLButtonElement>,\n ) => {\n event.preventDefault();\n event.stopPropagation();\n\n const targetPos = getNodePosFromHandle(event);\n if (targetPos && targetPos.pos !== null) {\n const resolvedPos = editor.state.doc.resolve(targetPos.pos);\n const startOfBlock = resolvedPos.end(resolvedPos.depth);\n editor\n .chain()\n .insertContentAt(startOfBlock, {\n type: \"paragraph\",\n content: [{ type: \"text\", text: \"/\" }],\n })\n .focus()\n .run();\n }\n };\n\n const handleSelectBlockAndOpenMenu = (\n event: React.MouseEvent<HTMLButtonElement>,\n ) => {\n const targetPos = getNodePosFromHandle(event);\n if (!targetPos || targetPos.pos === null) return;\n\n const resolvedPos = editor.state.doc.resolve(targetPos.pos);\n let targetDepth = resolvedPos.depth;\n\n for (let d = resolvedPos.depth; d > 0; d--) {\n const currentNodeName = resolvedPos.node(d)?.type.name;\n\n if (\n currentNodeName === \"blockquote\" ||\n currentNodeName === \"bulletList\" ||\n currentNodeName === \"orderedList\" ||\n currentNodeName === \"table\"\n ) {\n targetDepth = d;\n break;\n }\n }\n\n const blockStartPos = resolvedPos.before(targetDepth);\n const node = resolvedPos.node(targetDepth);\n\n if (blockStartPos !== undefined && node && node.type.name !== \"doc\") {\n const { tr } = editor.state;\n\n const selection = NodeSelection.create(editor.state.doc, blockStartPos);\n editor.view.dispatch(tr.setSelection(selection));\n\n setCurrentNodeType(node.type.name);\n console.log(\"Active Block Node Status:\", node.type.name);\n setIsOpen(true);\n }\n };\n\n return (\n <DragHandle\n className=\"drag-handle\"\n editor={editor}\n nested={false}\n computePositionConfig={DRAG_HANDLE_POSITION}\n >\n <div className=\"flex items-center gap-1 mr-2 \">\n <TooltipWraper content={t.drag.insertBlock}>\n <Button\n onClick={handleInsertAtDragPosition}\n variant={\"ghost\"}\n className=\"\"\n size={\"icon-sm\"}\n >\n <Plus />\n </Button>\n </TooltipWraper>\n\n <div className=\"relative\">\n <DropdownMenu open={isOpen} onOpenChange={setIsOpen}>\n <TooltipWraper\n className=\"max-w-30 text-center\"\n content=\"Click for options Hold for dragging\"\n >\n <Button\n onClick={handleSelectBlockAndOpenMenu}\n variant={\"ghost\"}\n size={\"icon-sm\"}\n >\n <GripVertical />\n </Button>\n </TooltipWraper>\n <DropdownMenuTrigger className=\"sr-only\">open</DropdownMenuTrigger>\n <DropdownContent editor={editor} />\n </DropdownMenu>\n </div>\n </div>\n </DragHandle>\n );\n};\n\nexport default Dragable;\n\nconst DropdownContent = ({ editor }: { editor: Editor }) => {\n const t = useMessages();\n const handleSetColor = (color: Color) => {\n handleAddRecentUsed(color, () => {}, t);\n\n if (color.type === \"text\") {\n editor\n .chain()\n .focus()\n .setColor(`var(--tt-color-text-${color.value})`)\n .run();\n return;\n }\n setUniversalBlockBackgroundColor(\n editor,\n `var(--tt-color-text-${color.value})`,\n );\n };\n\n return (\n <DropdownMenuContent\n align=\"center\"\n sideOffset={30}\n side=\"left\"\n className=\"w-55\"\n >\n <Colors setColor={handleSetColor} editor={editor} />\n <DropdownMenuItem className={cn(\"text-muted-foreground\")}>\n <RotateCcw /> {t.drag.resetFormatting}\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n onClick={() => duplicateSelectedNode(editor)}\n className={cn(\"text-muted-foreground\")}\n >\n <Copy /> {t.drag.duplicate}\n <DropdownMenuShortcut>⌘D</DropdownMenuShortcut>\n </DropdownMenuItem>\n <DropdownMenuItem\n onClick={() => copySelectedNodeToClipboard(editor)}\n className={cn(\"text-muted-foreground\")}\n >\n <Clipboard /> {t.drag.copyToClipboard}\n <DropdownMenuShortcut>⌘C</DropdownMenuShortcut>\n </DropdownMenuItem>\n <DropdownMenuItem disabled className={cn(\"text-muted-foreground\")}>\n <Link /> {t.drag.copyAnchorLink}\n <DropdownMenuShortcut>⌘^L</DropdownMenuShortcut>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem disabled className={cn(\"text-muted-foreground\")}>\n <Sparkles /> {t.drag.askAi}\n <DropdownMenuShortcut>⌘J</DropdownMenuShortcut>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n onClick={() => deleteSelectedNode(editor)}\n variant=\"destructive\"\n >\n <Trash /> {t.common.delete}\n <DropdownMenuShortcut>Del</DropdownMenuShortcut>\n </DropdownMenuItem>\n </DropdownMenuContent>\n );\n};\n","import { LucideIcon } from \"lucide-react\";\r\nimport TooltipWrapper from \"./TooltipWrapper\";\r\nimport { Button } from \"./ui/button\";\r\nimport { cn } from \"~/lib/utils\";\r\nimport React from \"react\";\r\n\r\ninterface ToggleButtonProps {\r\n onClick: () => void;\r\n isActive?: boolean;\r\n icon: LucideIcon | React.JSX.Element;\r\n className?: string;\r\n text?: string;\r\n showTooltip?: boolean;\r\n tooltip?: string;\r\n disabled?: boolean;\r\n size?:\r\n | \"icon\"\r\n | \"default\"\r\n | \"xs\"\r\n | \"sm\"\r\n | \"lg\"\r\n | \"icon-xs\"\r\n | \"icon-sm\"\r\n | \"icon-lg\"\r\n | null\r\n | undefined;\r\n [key: string]: any\r\n}\r\n\r\nconst ToggleButton = ({\r\n onClick,\r\n isActive,\r\n icon,\r\n className,\r\n text,\r\n size,\r\n showTooltip,\r\n tooltip,\r\n disabled,\r\n ...props\r\n}: ToggleButtonProps) => {\r\n\r\nconst renderIcon = () => {\r\n if (React.isValidElement<{ className?: string }>(icon)) {\r\n // Thu hẹp kiểu ngay ở isValidElement thay vì ép kiểu bên trong: React\r\n // chỉ biết đây là ReactElement<unknown>, nên không có cách nào khẳng\r\n // định nó nhận className mà không nói ra.\r\n return React.cloneElement(icon, {\r\n className: cn(\"stroke-[2.75]\", icon.props.className),\r\n });\r\n }\r\n \r\n const IconComponent = icon as LucideIcon;\r\n return <IconComponent className=\"stroke-[2.75]\" />;\r\n };\r\n\r\n return (\r\n <TooltipWrapper content={tooltip} showTooltip={showTooltip}>\r\n <Button\r\n size={size ? size : text ? \"default\" : \"icon\"}\r\n variant={isActive ? \"secondary\" : \"ghost\"}\r\n className={cn(\"\",className)}\r\n onClick={onClick}\r\n disabled={disabled}\r\n {...props}\r\n >\r\n {renderIcon()} {text}\r\n </Button>\r\n </TooltipWrapper>\r\n );\r\n};\r\n\r\nexport default ToggleButton;\r\n","import * as React from \"react\"\r\nimport { Separator as SeparatorPrimitive } from \"radix-ui\"\r\n\r\nimport { cn } from \"~/lib/utils\"\r\n\r\nfunction Separator({\r\n className,\r\n orientation = \"horizontal\",\r\n decorative = true,\r\n ...props\r\n}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {\r\n return (\r\n <SeparatorPrimitive.Root\r\n data-slot=\"separator\"\r\n decorative={decorative}\r\n orientation={orientation}\r\n className={cn(\r\n \"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport { Separator }\r\n","export const AlignLeft = (props: any) => {\r\n return (\r\n <svg\r\n {...props}\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n d=\"M4 2C4 1.44772 3.55228 1 3 1C2.44772 1 2 1.44772 2 2V22C2 22.5523 2.44772 23 3 23C3.55228 23 4 22.5523 4 22V2Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n fill-rule=\"evenodd\"\r\n clipRule=\"evenodd\"\r\n d=\"M10 4C8.34315 4 7 5.34315 7 7V17C7 18.6569 8.34315 20 10 20H19C20.6569 20 22 18.6569 22 17V7C22 5.34315 20.6569 4 19 4H10ZM9 7C9 6.44772 9.44772 6 10 6H19C19.5523 6 20 6.44772 20 7V17C20 17.5523 19.5523 18 19 18H10C9.44772 18 9 17.5523 9 17V7Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n\r\nexport const AlignRight = (props: any) => {\r\n return (\r\n <svg\r\n {...props}\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n fill-rule=\"evenodd\"\r\n clipRule=\"evenodd\"\r\n d=\"M21 1C21.5523 1 22 1.44772 22 2V22C22 22.5523 21.5523 23 21 23C20.4477 23 20 22.5523 20 22V2C20 1.44772 20.4477 1 21 1Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n fill-rule=\"evenodd\"\r\n clipRule=\"evenodd\"\r\n d=\"M2 7C2 5.34315 3.34315 4 5 4H14C15.6569 4 17 5.34315 17 7V17C17 18.6569 15.6569 20 14 20H5C3.34315 20 2 18.6569 2 17V7ZM5 6C4.44772 6 4 6.44772 4 7V17C4 17.5523 4.44772 18 5 18H14C14.5523 18 15 17.5523 15 17V7C15 6.44772 14.5523 6 14 6H5Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n\r\nexport const AlignCenter = (props: any) => {\r\n return (\r\n <svg\r\n {...props}\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n d=\"M12 1C12.5523 1 13 1.44772 13 2V22C13 22.5523 12.5523 23 12 23C11.4477 23 11 22.5523 11 22V2C11 1.44772 11.4477 1 12 1Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M2 7C2 5.34315 3.34315 4 5 4H7C7.55228 4 8 4.44772 8 5C8 5.55228 7.55228 6 7 6H5C4.44772 6 4 6.44772 4 7V17C4 17.5523 4.44772 18 5 18H7C7.55228 18 8 18.4477 8 19C8 19.5523 7.55228 20 7 20H5C3.34315 20 2 18.6569 2 17V7Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n d=\"M19 4C20.6569 4 22 5.34315 22 7V17C22 18.6569 20.6569 20 19 20H17C16.4477 20 16 19.5523 16 19C16 18.4477 16.4477 18 17 18H19C19.5523 18 20 17.5523 20 17V7C20 6.44772 19.5523 6 19 6H17C16.4477 6 16 5.55228 16 5C16 4.44772 16.4477 4 17 4H19Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\nexport const CaptionIcon = (props: any) => {\r\n return (\r\n <svg\r\n {...props}\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path\r\n fillRule=\"evenodd\"\r\n clipRule=\"evenodd\"\r\n d=\"M7 22C7 21.4477 7.44772 21 8 21H16C16.5523 21 17 21.4477 17 22C17 22.5523 16.5523 23 16 23H8C7.44772 23 7 22.5523 7 22Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n fillRule=\"evenodd\"\r\n clipRule=\"evenodd\"\r\n d=\"M3 18C3 17.4477 3.44772 17 4 17H20C20.5523 17 21 17.4477 21 18C21 18.5523 20.5523 19 20 19H4C3.44772 19 3 18.5523 3 18Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n <path\r\n fillRule=\"evenodd\"\r\n clipRule=\"evenodd\"\r\n d=\"M3 5C3 3.34315 4.34315 2 6 2H18C19.6569 2 21 3.34315 21 5V11C21 12.6569 19.6569 14 18 14H6C4.34315 14 3 12.6569 3 11V5ZM6 4C5.44772 4 5 4.44772 5 5V11C5 11.5523 5.44772 12 6 12H18C18.5523 12 19 11.5523 19 11V5C19 4.44772 18.5523 4 18 4H6Z\"\r\n fill=\"currentColor\"\r\n ></path>\r\n </svg>\r\n );\r\n};\r\n","import { Editor } from \"@tiptap/core\";\r\nimport { BubbleMenu } from \"@tiptap/react/menus\";\r\nimport ToggleButton from \"../ToggleButton\";\r\nimport { Download, RefreshCcw, Trash2 } from \"lucide-react\";\r\nimport { Separator } from \"../ui/separator\";\r\nimport { AlignCenter, AlignLeft, AlignRight } from \"./icon\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\ninterface ImageBubbleMenuProps {\r\n editor: Editor | null;\r\n onReplaceClick: () => void;\r\n}\r\n\r\nconst ImageBubbleMenu = ({ editor, onReplaceClick }: ImageBubbleMenuProps) => {\r\n const t = useMessages();\r\n if (!editor) {\r\n return;\r\n }\r\n\r\n const { alignment } = editor.getAttributes(\"image\");\r\n const setAlignment = (nextAlignment: string) => {\r\n editor\r\n .chain()\r\n .focus()\r\n .updateAttributes(\"image\", { alignment: nextAlignment })\r\n .run();\r\n };\r\n\r\n const onReplace = () => {\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteNode(\"image\")\r\n .insertContent({ type: \"imageUpload\" })\r\n .run();\r\n };\r\n\r\n return (\r\n <BubbleMenu\r\n pluginKey=\"imageMenu\"\r\n editor={editor}\r\n shouldShow={({ editor, view }) => {\r\n return view.hasFocus() && editor.isActive(\"image\");\r\n }}\r\n options={{\r\n offset: 6,\r\n }}\r\n className=\"bg-card not-prose border shadow-xl rounded-xl px-0.5 flex items-center gap-1 py-0.5\"\r\n >\r\n <div className=\" flex items-center gap-1 \">\r\n <ToggleButton\r\n tooltip={t.align.left}\r\n icon={<AlignLeft />}\r\n isActive={alignment === \"left\"}\r\n onClick={() => setAlignment(\"left\")}\r\n />\r\n <ToggleButton\r\n tooltip={t.align.center}\r\n icon={<AlignCenter />}\r\n isActive={alignment === \"center\"}\r\n onClick={() => setAlignment(\"center\")}\r\n />\r\n <ToggleButton\r\n tooltip={t.align.right}\r\n icon={<AlignRight />}\r\n isActive={alignment === \"right\"}\r\n onClick={() => setAlignment(\"right\")}\r\n />\r\n </div>\r\n {/* <Separator orientation=\"vertical\" />\r\n <div className=\"flex gap-0.5\">\r\n <ToggleButton\r\n tooltip=\"Caption\"\r\n icon={<CaptionIcon />}\r\n isActive={caption}\r\n onClick={() => setCaption(!caption)}\r\n />\r\n </div> */}\r\n <Separator orientation=\"vertical\" />\r\n <div className=\"flex gap-0.5\">\r\n <ToggleButton\r\n tooltip={t.image.download}\r\n icon={Download}\r\n // isActive={isBold}\r\n onClick={() => editor.chain().focus().toggleBold().run()}\r\n />\r\n <ToggleButton tooltip=\"Replace\" icon={RefreshCcw} onClick={onReplace} />\r\n <ToggleButton\r\n variant=\"destructive\"\r\n tooltip={t.image.delete}\r\n icon={Trash2}\r\n // isActive={isBold}\r\n onClick={() => editor.chain().focus().deleteSelection().run()}\r\n />\r\n </div>\r\n </BubbleMenu>\r\n );\r\n};\r\n\r\nexport default ImageBubbleMenu;\r\n\r\n// const ToggleButton = ({\r\n// onClick,\r\n// isActive,\r\n// icon,\r\n// className,\r\n// text,\r\n// size,\r\n// showTooltip,\r\n// tooltip,\r\n// disabled,\r\n// }: ToggleButtonProps) => {\r\n// const Icon = icon;\r\n\r\n// return (\r\n// <TooltipWraper content={tooltip} showTooltip={showTooltip}>\r\n// <Button\r\n// size={size ? size : text ? \"default\" : \"icon\"}\r\n// variant={isActive ? \"secondary\" : \"ghost\"}\r\n// className={cn( className)}\r\n// onClick={onClick}\r\n// disabled={disabled}\r\n// >\r\n// <Icon className=\"stroke-[2.75] \"/> {text}\r\n// </Button>\r\n// </TooltipWraper>\r\n// );\r\n// };\r\n","import * as React from \"react\"\r\nimport { Popover as PopoverPrimitive } from \"radix-ui\"\r\n\r\nimport { cn } from \"~/lib/utils\"\r\n\r\nfunction Popover({\r\n ...props\r\n}: React.ComponentProps<typeof PopoverPrimitive.Root>) {\r\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />\r\n}\r\n\r\nfunction PopoverTrigger({\r\n ...props\r\n}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\r\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\r\n}\r\n\r\nfunction PopoverContent({\r\n className,\r\n align = \"center\",\r\n sideOffset = 4,\r\n ...props\r\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\r\n return (\r\n <PopoverPrimitive.Portal>\r\n <PopoverPrimitive.Content\r\n data-slot=\"popover-content\"\r\n align={align}\r\n sideOffset={sideOffset}\r\n className={cn(\r\n \"z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n </PopoverPrimitive.Portal>\r\n )\r\n}\r\n\r\nfunction PopoverAnchor({\r\n ...props\r\n}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\r\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />\r\n}\r\n\r\nfunction PopoverHeader({ className, ...props }: React.ComponentProps<\"div\">) {\r\n return (\r\n <div\r\n data-slot=\"popover-header\"\r\n className={cn(\"flex flex-col gap-0.5 text-sm\", className)}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction PopoverTitle({ className, ...props }: React.ComponentProps<\"h2\">) {\r\n return (\r\n <div\r\n data-slot=\"popover-title\"\r\n className={cn(\"font-medium\", className)}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction PopoverDescription({\r\n className,\r\n ...props\r\n}: React.ComponentProps<\"p\">) {\r\n return (\r\n <p\r\n data-slot=\"popover-description\"\r\n className={cn(\"text-muted-foreground\", className)}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport {\r\n Popover,\r\n PopoverAnchor,\r\n PopoverContent,\r\n PopoverDescription,\r\n PopoverHeader,\r\n PopoverTitle,\r\n PopoverTrigger,\r\n}\r\n","import React, { useCallback, useState, useEffect } from \"react\";\r\nimport { Popover, PopoverContent, PopoverTrigger } from \"../ui/popover\";\r\nimport { CornerDownLeft, ExternalLink, Link, Trash } from \"lucide-react\";\r\nimport TooltipWraper from \"../TooltipWrapper\";\r\nimport { Button } from \"../ui/button\";\r\nimport { Editor } from \"@tiptap/core\";\r\nimport { Separator } from \"../ui/separator\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\nimport { cn } from \"~/lib/utils\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\nconst PickLink = ({ editor }: { editor: Editor }) => {\r\n const t = useMessages();\r\n const [isOpen, setIsOpen] = useState(false);\r\n const [url, setUrl] = useState(\"\");\r\n\r\n const { isLink, activeUrl } = useEditorState({\r\n editor,\r\n selector: (ctx) => ({\r\n isLink: ctx.editor.isActive(\"link\"),\r\n activeUrl: ctx.editor.getAttributes(\"link\").href || \"\",\r\n }),\r\n });\r\n\r\n useEffect(() => {\r\n if (isOpen) {\r\n setUrl(activeUrl);\r\n }\r\n }, [isOpen, activeUrl]);\r\n\r\n const setLink = useCallback(() => {\r\n if (!url) return;\r\n\r\n try {\r\n editor\r\n .chain()\r\n .focus()\r\n .extendMarkRange(\"link\")\r\n .setLink({ href: url })\r\n .run();\r\n\r\n setIsOpen(false);\r\n } catch (e) {\r\n console.error(e);\r\n }\r\n }, [editor, url]);\r\n\r\n const unsetLink = useCallback(() => {\r\n try {\r\n editor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\r\n setUrl(\"\");\r\n setIsOpen(false);\r\n } catch (e) {\r\n console.error(e);\r\n }\r\n }, [editor]);\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent) => {\r\n if (e.key === \"Enter\") {\r\n e.preventDefault();\r\n setLink();\r\n }\r\n };\r\n\r\n return (\r\n <Popover open={isOpen} onOpenChange={setIsOpen}>\r\n <TooltipWraper content={t.marks.link}>\r\n <PopoverTrigger asChild>\r\n <Button variant={isLink ? \"secondary\" : \"ghost\"} size={\"icon\"}>\r\n <Link className={cn(isLink && \"text-blue-500\")} />\r\n </Button>\r\n </PopoverTrigger>\r\n </TooltipWraper>\r\n <PopoverContent\r\n className=\"p-1.5 rounded-xl w-78.75 shadow-xl\"\r\n side=\"bottom\"\r\n align=\"center\"\r\n >\r\n <div className=\"flex items-center gap-1\">\r\n <input\r\n value={url}\r\n onChange={(e) => setUrl(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n className=\"border-0 outline-0 min-w-48 text-sm w-full h-8 py-1 pl-2.5 pr-0 bg-transparent\"\r\n placeholder={t.bubble.linkPlaceholder}\r\n autoFocus\r\n />\r\n <div>\r\n <Button\r\n disabled={!url}\r\n onClick={setLink}\r\n variant={\"ghost\"}\r\n size={\"icon\"}\r\n type=\"button\"\r\n >\r\n <CornerDownLeft />\r\n </Button>\r\n </div>\r\n <Separator orientation=\"vertical\" />\r\n <div className=\"flex gap-0.5\">\r\n <Button disabled={!url} variant={\"ghost\"} size={\"icon\"} asChild>\r\n <a\r\n href={url || undefined}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className={cn(!url && \"pointer-events-none opacity-50\")}\r\n >\r\n <ExternalLink />\r\n </a>\r\n </Button>\r\n <Button\r\n disabled={!isLink}\r\n onClick={unsetLink}\r\n variant={\"destructive\"}\r\n size={\"icon\"}\r\n >\r\n <Trash />\r\n </Button>\r\n </div>\r\n </div>\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n};\r\n\r\nexport default PickLink;\r\n","import ToggleButton from \"../ToggleButton\";\r\nimport { Editor, } from \"@tiptap/react\";\r\nimport { ListIndentDecrease, ListIndentIncrease } from \"lucide-react\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\nconst IndentButtons = ({ editor }: { editor: Editor }) => {\r\n const t = useMessages();\r\n const currentLevel =\r\n editor.getAttributes(\"paragraph\")[\"data-indent-level\"] ||\r\n editor.getAttributes(\"heading\")[\"data-indent-level\"] ||\r\n 0;\r\n\r\n const isMinIndent = currentLevel <= 0;\r\n const isMaxIndent = currentLevel >= 10;\r\n return (\r\n <div className=\"flex gap-0.5\">\r\n <ToggleButton\r\n tooltip={t.bubble.decreaseIndent}\r\n disabled={isMinIndent}\r\n icon={ListIndentDecrease}\r\n onClick={() => editor.chain().focus().outdent().run()}\r\n />\r\n <ToggleButton\r\n tooltip={t.bubble.increaseIndent}\r\n disabled={isMaxIndent}\r\n icon={ListIndentIncrease}\r\n onClick={() => editor.chain().focus().indent().run()}\r\n />\r\n </div>\r\n );\r\n};\r\n\r\nexport default IndentButtons;\r\n","import { useState } from \"react\";\r\nimport TooltipWraper from \"../TooltipWrapper\";\r\nimport { Popover, PopoverContent, PopoverTrigger } from \"../ui/popover\";\r\nimport { Button } from \"../ui/button\";\r\nimport {\r\n EllipsisVertical,\r\n Subscript,\r\n Superscript,\r\n TextAlignCenter,\r\n TextAlignEnd,\r\n TextAlignJustify,\r\n TextAlignStart,\r\n} from \"lucide-react\";\r\nimport { Editor } from \"@tiptap/core\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\nimport { Separator } from \"../ui/separator\";\r\nimport ToggleButton from \"../ToggleButton\";\r\nimport IndentButtons from \"./IndentButtons\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\nconst BubbleMoreOption = ({ editor }: { editor: Editor }) => {\r\n const t = useMessages();\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const {\r\n isSubscript,\r\n isSuperscript,\r\n isAlignLeft,\r\n isAlignRight,\r\n isAlignCenter,\r\n isAlignJustify,\r\n } = useEditorState({\r\n editor,\r\n selector: (ctx) => ({\r\n isSubscript: ctx.editor.isActive(\"subscript\"),\r\n isSuperscript: ctx.editor.isActive(\"superscript\"),\r\n isAlignLeft: ctx.editor.isActive({ textAlign: \"left\" }),\r\n isAlignRight: ctx.editor.isActive({ textAlign: \"right\" }),\r\n isAlignCenter: ctx.editor.isActive({ textAlign: \"center\" }),\r\n isAlignJustify: ctx.editor.isActive({ textAlign: \"justify\" }),\r\n }),\r\n });\r\n\r\n return (\r\n <Popover open={isOpen} onOpenChange={setIsOpen}>\r\n <TooltipWraper content={t.common.moreOptions}>\r\n <PopoverTrigger asChild>\r\n <Button variant={\"ghost\"} size={\"icon\"}>\r\n <EllipsisVertical />\r\n </Button>\r\n </PopoverTrigger>\r\n </TooltipWraper>\r\n <PopoverContent\r\n className=\"p-1 rounded-xl max-w-fit shadow-xl\"\r\n side=\"top\"\r\n align=\"center\"\r\n onOpenAutoFocus={(e) => e.preventDefault()}\r\n >\r\n <div className=\"flex gap-0.5\">\r\n <div className=\"flex gap-0.5\">\r\n <ToggleButton\r\n tooltip=\"Subscript\"\r\n isActive={isSubscript}\r\n icon={Subscript}\r\n onClick={() => editor.chain().focus().toggleSubscript().run()}\r\n />\r\n <ToggleButton\r\n tooltip=\"Superscript\"\r\n isActive={isSuperscript}\r\n icon={Superscript}\r\n onClick={() => editor.chain().focus().toggleSuperscript().run()}\r\n />\r\n </div>\r\n <Separator orientation=\"vertical\" />\r\n <div className=\"flex gap-0.5\">\r\n <ToggleButton\r\n tooltip={t.align.left}\r\n isActive={isAlignLeft}\r\n icon={TextAlignStart}\r\n onClick={() =>\r\n editor.chain().focus().toggleTextAlign(\"left\").run()\r\n }\r\n />\r\n <ToggleButton\r\n tooltip={t.align.center}\r\n isActive={isAlignCenter}\r\n icon={TextAlignCenter}\r\n onClick={() =>\r\n editor.chain().focus().toggleTextAlign(\"center\").run()\r\n }\r\n />\r\n <ToggleButton\r\n tooltip={t.align.right}\r\n isActive={isAlignRight}\r\n icon={TextAlignEnd}\r\n onClick={() =>\r\n editor.chain().focus().toggleTextAlign(\"right\").run()\r\n }\r\n />\r\n <ToggleButton\r\n tooltip={t.align.justify}\r\n isActive={isAlignJustify}\r\n icon={TextAlignJustify}\r\n onClick={() =>\r\n editor.chain().focus().toggleTextAlign(\"justify\").run()\r\n }\r\n />\r\n </div>\r\n <Separator orientation=\"vertical\" />\r\n <IndentButtons editor={editor} />\r\n </div>\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n};\r\n\r\nexport default BubbleMoreOption;\r\n","import { CSSProperties } from \"react\";\r\nimport TooltipWraper from \"../TooltipWrapper\";\r\nimport { Button } from \"../ui/button\";\r\nimport { TextIcon } from \"./icon\";\r\nimport { Editor } from \"@tiptap/core\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\n\r\ninterface TextButtonProps {\r\n tooltip: string;\r\n color: string;\r\n onClick: () => void;\r\n editor: Editor;\r\n}\r\n\r\nconst TextButton = ({ tooltip, color, onClick, editor }: TextButtonProps) => {\r\n const { isActive } = useEditorState({\r\n editor,\r\n selector: (ctx) => ({\r\n isActive: ctx.editor.isActive(\"textStyle\", {\r\n color: `var(--tt-color-text-${color})`,\r\n }),\r\n }),\r\n });\r\n\r\n return (\r\n <>\r\n <TooltipWraper content={tooltip}>\r\n <Button\r\n size={\"icon\"}\r\n variant={isActive ? \"secondary\" : \"ghost\"}\r\n style={\r\n {\r\n \"--color-text-button-color\": `var(--tt-color-text-${color})`,\r\n } as CSSProperties\r\n }\r\n onClick={onClick}\r\n >\r\n <span\r\n className=\"size-5 flex items-center justify-center rounded-full tiptap-button-color-text\"\r\n style={{\r\n color: `var(--tt-color-text-${color})`,\r\n }}\r\n >\r\n <TextIcon />\r\n </span>\r\n </Button>\r\n </TooltipWraper>\r\n </>\r\n );\r\n};\r\n\r\nexport default TextButton;\r\n","import { CSSProperties } from \"react\";\r\nimport TooltipWraper from \"../TooltipWrapper\";\r\nimport { Button } from \"../ui/button\";\r\nimport { Editor, useEditorState } from \"@tiptap/react\";\r\n\r\ntype Color = {\r\n name: string;\r\n value: string;\r\n type: string;\r\n};\r\n\r\ninterface HighlightButtonProps {\r\n tooltip: string;\r\n color: Color;\r\n onClick: () => void;\r\n isActive?: boolean;\r\n editor: Editor;\r\n}\r\nconst HighlightButton = ({\r\n tooltip,\r\n color,\r\n onClick,\r\n editor,\r\n}: HighlightButtonProps) => {\r\n const { isActive } = useEditorState({\r\n editor,\r\n selector: (ctx) => ({\r\n isActive: ctx.editor.isActive(\"highlight\", {\r\n color:\r\n color.value === \"default\"\r\n ? \"var(--background)\"\r\n : `var(--tt-color-highlight-${color.value})`,\r\n }),\r\n }),\r\n });\r\n\r\n return (\r\n <>\r\n <TooltipWraper content={tooltip}>\r\n <Button\r\n size={\"icon\"}\r\n variant={isActive ? \"secondary\" : \"ghost\"}\r\n onClick={onClick}\r\n >\r\n <span\r\n className=\"size-5 flex items-center justify-center rounded-full tiptap-button-highlight\"\r\n style={\r\n {\r\n \"--highlight-color\":\r\n color.value === \"default\"\r\n ? \"var(--background)\"\r\n : `var(--tt-color-highlight-${color.value})`,\r\n } as CSSProperties\r\n }\r\n ></span>\r\n </Button>\r\n </TooltipWraper>\r\n </>\r\n );\r\n};\r\n\r\nexport default HighlightButton;\r\n","import { CSSProperties, useState } from \"react\";\nimport { ChevronDown } from \"lucide-react\";\nimport TooltipWraper from \"../TooltipWrapper\";\nimport { Button } from \"../ui/button\";\nimport { ScrollArea } from \"../ui/scroll-area\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../ui/popover\";\nimport { Editor } from \"@tiptap/core\";\nimport TextButton from \"./TextButton\";\nimport HighlightButton from \"./HighlightButton\";\nimport { TextIcon } from \"./icon\";\nimport { useEditorState } from \"@tiptap/react\";\nimport { Color, GroupColors } from \"./type\";\nimport { createGroupColors } from \"./constant\";\nimport { getRecentColors, handleAddRecentUsed } from \"./utils\";\nimport { useMessages } from \"~/i18n\";\n\n\nconst ColorDropdown = ({ editor }: { editor: Editor }) => {\n const t = useMessages();\n\n const [items, setItems] = useState<GroupColors[]>(() => {\n const recent = getRecentColors();\n if (recent.length > 0) {\n return [{ title: t.common.recentlyUsed, colors: recent }, ...createGroupColors(t)];\n }\n return createGroupColors(t);\n });\n\n const { textColor, highlightColor } = useEditorState({\n editor,\n selector: (ctx) => ({\n textColor: ctx.editor.getAttributes(\"textStyle\").color,\n highlightColor: ctx.editor.getAttributes(\"highlight\"),\n }),\n });\n\n\n return (\n <Popover>\n <TooltipWraper content={t.bubble.textColor}>\n <PopoverTrigger asChild>\n <Button variant={\"ghost\"} className=\"p-2 gap-1\">\n <span\n className=\"size-5 flex items-center justify-center border rounded-full \n tiptap-button-color-text-popover\"\n style={\n {\n \"--active-highlight-color\": highlightColor?.color,\n } as CSSProperties\n }\n >\n <TextIcon style={{ color: textColor }} />\n </span>\n <ChevronDown className=\"size-3\" size={10} />\n </Button>\n </PopoverTrigger>\n </TooltipWraper>\n <DropdwonContent\n items={items}\n editor={editor}\n handleAddRecentUsed={(color: Color) =>\n handleAddRecentUsed(color, setItems, t)\n }\n />\n </Popover>\n );\n};\n\nexport default ColorDropdown;\n\nconst DropdwonContent = ({\n items,\n editor,\n handleAddRecentUsed,\n}: {\n items: GroupColors[];\n editor: Editor;\n handleAddRecentUsed: (color: Color) => void;\n}) => {\n return (\n <PopoverContent\n onOpenAutoFocus={(e) => e.preventDefault()}\n className=\"w-47 rounded-2xl p-0 pr-px bg-popover shadow-2xl z-50 overflow-hidden\"\n >\n <ScrollArea className=\"h-66.25 p-1.5 pr-2.5\">\n {items.map((item, idx) => (\n <div key={item.title}>\n <div className=\"min-w-max flex relative flex-col justify-center align-middle\">\n <div className=\"capitalize pt-3 pb-1 px-1.5 leading-normal font-medium text-xs text-muted-foreground\">\n {item.title}\n </div>\n <div className=\"grid grid-cols-5 gap-0.5 pl-1\">\n {item.colors.map((color) =>\n color.type === \"text\" ? (\n <TextButton\n editor={editor}\n color={color.value}\n tooltip={`${color.name} ${color.type}`}\n onClick={() => {\n editor\n .chain()\n .focus()\n .setColor(`var(--tt-color-text-${color.value})`)\n .run();\n handleAddRecentUsed(color);\n }}\n />\n ) : color.type === \"background\" ? (\n <HighlightButton\n editor={editor}\n color={color}\n tooltip={`${color.name} ${color.type}`}\n onClick={() => {\n editor\n .chain()\n .focus()\n .toggleHighlight({\n color: `var(--tt-color-highlight-${color.value})`,\n })\n .run();\n handleAddRecentUsed(color);\n }}\n />\n ) : (\n \"\"\n ),\n )}\n </div>\n </div>\n </div>\n ))}\n </ScrollArea>\n </PopoverContent>\n );\n};\n","import type { BlockItem } from \"./types\";\r\nimport {\r\n Heading1,\r\n Heading2,\r\n Heading3,\r\n DivideIcon,\r\n TextQuote,\r\n FootprintsIcon,\r\n EraserIcon,\r\n Type,\r\n} from \"lucide-react\";\r\n\r\nexport const text: BlockItem = {\r\n title: \"Text\",\r\n description: \"Just start typing with plain text.\",\r\n searchTerms: [\"p\", \"paragraph\"],\r\n icon: <Type />,\r\n command: ({ editor, range }) => {\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteRange(range)\r\n .toggleNode(\"paragraph\", \"paragraph\")\r\n .run();\r\n },\r\n};\r\n\r\nexport const heading1: BlockItem = {\r\n title: \"Heading 1\",\r\n description: \"Big heading.\",\r\n searchTerms: [\"h1\", \"title\", \"big\", \"large\"],\r\n icon: <Heading1 />,\r\n command: ({ editor, range }) => {\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteRange(range)\r\n .setNode(\"heading\", { level: 1 })\r\n .run();\r\n },\r\n};\r\n\r\nexport const heading2: BlockItem = {\r\n title: \"Heading 2\",\r\n description: \"Medium heading.\",\r\n searchTerms: [\"h2\", \"subtitle\", \"medium\"],\r\n icon: <Heading2 />,\r\n command: ({ editor, range }) => {\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteRange(range)\r\n .setNode(\"heading\", { level: 2 })\r\n .run();\r\n },\r\n};\r\n\r\nexport const heading3: BlockItem = {\r\n title: \"Heading 3\",\r\n description: \"Small heading.\",\r\n searchTerms: [\"h3\", \"subtitle\", \"small\"],\r\n icon: <Heading3 />,\r\n command: ({ editor, range }) => {\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteRange(range)\r\n .setNode(\"heading\", { level: 3 })\r\n .run();\r\n },\r\n};\r\n\r\nexport const hardBreak: BlockItem = {\r\n title: \"Hard Break\",\r\n description: \"Add a break between lines.\",\r\n searchTerms: [\"break\", \"line\"],\r\n icon: <DivideIcon />,\r\n command: ({ editor, range }) => {\r\n editor.chain().focus().deleteRange(range).setHardBreak().run();\r\n },\r\n};\r\n\r\nexport const blockquote: BlockItem = {\r\n title: \"Blockquote\",\r\n description: \"Add blockquote.\",\r\n searchTerms: [\"quote\", \"blockquote\"],\r\n icon: <TextQuote />,\r\n command: ({ editor, range }) => {\r\n editor.chain().focus().deleteRange(range).toggleBlockquote().run();\r\n },\r\n};\r\n// Bỏ block `footer`: nó gọi setFooter, lệnh không tồn tại ở bất kỳ extension\r\n// nào — tàn dư template, và một // @ts-ignore đã che nó suốt. Không có menu\r\n// nào trỏ tới nên chưa ai vấp phải.\r\n\r\nexport const clearLine: BlockItem = {\r\n title: \"Clear Line\",\r\n description: \"Clear the current line.\",\r\n searchTerms: [\"clear\", \"line\"],\r\n icon: <EraserIcon />,\r\n command: ({ editor, range }) => {\r\n editor.chain().focus().selectParentNode().deleteSelection().run();\r\n },\r\n};\r\n","import type { BlockItem } from \"./types\";\r\nimport { List, ListOrdered, ListTodo } from \"lucide-react\";\r\n\r\nexport const bulletList: BlockItem = {\r\n title: \"Bullet List\",\r\n description: \"Create a simple bullet list.\",\r\n searchTerms: [\"unordered\", \"point\"],\r\n icon: <List />,\r\n command: ({ editor, range }) => {\r\n // @ts-ignore\r\n editor.chain().focus().deleteRange(range).toggleBulletList().run();\r\n },\r\n};\r\n\r\nexport const orderedList: BlockItem = {\r\n title: \"Numbered List\",\r\n description: \"Create a list with numbering.\",\r\n searchTerms: [\"ordered\"],\r\n icon: <ListOrdered />,\r\n command: ({ editor, range }) => {\r\n // @ts-ignore\r\n editor.chain().focus().deleteRange(range).toggleOrderedList().run();\r\n },\r\n};\r\n\r\nexport const taskList: BlockItem = {\r\n title: \"Tod-do list\",\r\n description: \"Create a todo list\",\r\n searchTerms: [\"list\", \"task\", \"todo\"],\r\n icon: <ListTodo />,\r\n command: ({editor, range}) => {\r\n editor.chain().focus().deleteRange(range).toggleTaskList().run()\r\n }\r\n};\r\n","import { CodeXmlIcon } from 'lucide-react';\r\nimport { BlockItem } from './types';\r\n\r\nexport const htmlCodeBlock: BlockItem = {\r\n title: 'Code Block',\r\n description: 'Insert a custom HTML block',\r\n searchTerms: ['html', 'code', 'custom'],\r\n icon: <CodeXmlIcon />,\r\n command: ({ editor, range }) => {\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteRange(range)\r\n // Trước đây gọi setHtmlCodeBlock — lệnh đó không tồn tại ở extension\r\n // nào, chỉ là tàn dư của template và bị // @ts-ignore che đi.\r\n .toggleCodeBlock()\r\n .run();\r\n },\r\n};\r\n","import { Popover, PopoverTrigger, PopoverContent } from \"../ui/popover\";\r\nimport { ScrollArea } from \"../ui/scroll-area\";\r\nimport { BlockGroupItem } from \"../blocks/types\";\r\nimport { useMessages, type Messages } from \"~/i18n\";\r\nimport { heading1, heading2, heading3, text } from \"../blocks/typography\";\r\nimport { bulletList, orderedList, taskList } from \"../blocks/list\";\r\nimport { htmlCodeBlock } from \"../blocks/code\";\r\nimport {\r\n ChevronDown,\r\n CodeXmlIcon,\r\n Heading1,\r\n Heading2,\r\n Heading3,\r\n List,\r\n ListOrdered,\r\n ListTodo,\r\n LucideIcon,\r\n TextQuote,\r\n Type,\r\n} from \"lucide-react\";\r\nimport { Button } from \"../ui/button\";\r\nimport { Editor } from \"@tiptap/core\";\r\nimport { Level } from \"@tiptap/extension-heading\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\n\r\n/** Nhãn lấy từ từ điển, nên menu này và slash menu luôn gọi cùng một tên. */\r\nconst buildItems = (t: Messages): BlockGroupItem[] => [\r\n {\r\n title: t.bubble.turnInto,\r\n commands: [\r\n { ...text, ...t.blocks.text },\r\n { ...heading1, ...t.blocks.heading1 },\r\n { ...heading2, ...t.blocks.heading2 },\r\n { ...heading3, ...t.blocks.heading3 },\r\n { ...bulletList, ...t.blocks.bulletList },\r\n { ...orderedList, ...t.blocks.orderedList },\r\n { ...taskList, ...t.blocks.taskList },\r\n { ...htmlCodeBlock, ...t.blocks.codeBlock },\r\n ],\r\n },\r\n];\r\n\r\ninterface Headings {\r\n title: string;\r\n icon: LucideIcon;\r\n level: Level;\r\n}\r\n\r\nconst buildHeadings = (t: Messages): Headings[] => [\r\n { title: t.blocks.heading1.title, icon: Heading1, level: 1 },\r\n { title: t.blocks.heading2.title, icon: Heading2, level: 2 },\r\n { title: t.blocks.heading3.title, icon: Heading3, level: 3 },\r\n];\r\n\r\nconst TextTurnInto = ({ editor }: { editor: Editor }) => {\r\n const t = useMessages();\r\n const headings = buildHeadings(t);\r\n const items = buildItems(t);\r\n const {\r\n activeBlock,\r\n isParagraph,\r\n isBulletList,\r\n isOrderedList,\r\n isTaskList,\r\n isBlockquote,\r\n isCodeBlock,\r\n headingLevel,\r\n } = useEditorState({\r\n editor,\r\n selector: (ctx) => {\r\n const activeHeading = headings.find((h) =>\r\n ctx.editor.isActive(\"heading\", { level: h.level }),\r\n );\r\n return {\r\n activeBlock: ctx.editor.getAttributes(\"paragraph\"),\r\n isParagraph: ctx.editor.isActive(\"paragraph\"),\r\n isBulletList: ctx.editor.isActive(\"bulletList\"),\r\n isOrderedList: ctx.editor.isActive(\"orderedList\"),\r\n isTaskList: ctx.editor.isActive(\"taskList\"),\r\n isBlockquote: ctx.editor.isActive(\"blockquote\"),\r\n isCodeBlock: ctx.editor.isActive(\"codeBlock\"),\r\n headingLevel: activeHeading ? activeHeading.title : null,\r\n };\r\n },\r\n });\r\n\r\n const getTriggerLabel = () => {\r\n if (isParagraph) return t.blocks.text.title;\r\n if (headingLevel) return headingLevel;\r\n if (isBulletList) return t.blocks.bulletList.title;\r\n if (isOrderedList) return t.blocks.orderedList.title;\r\n if (isTaskList) return \"To-do list\";\r\n if (isBlockquote) return t.blocks.blockquote.title;\r\n if (isCodeBlock) return t.blocks.codeBlock.title;\r\n return t.blocks.text.title;\r\n };\r\n\r\n return (\r\n <Popover>\r\n <PopoverTrigger asChild>\r\n <Button variant={\"ghost\"}>\r\n {getTriggerLabel()} <ChevronDown />\r\n </Button>\r\n </PopoverTrigger>\r\n <PopoverContent className=\"w-48 rounded-2xl p-0 pr-px bg-popover shadow-2xl z-50 overflow-hidden\">\r\n <ScrollArea className=\"h-66.25 p-1.5 pr-2.5\">\r\n {items.map((item, idx) => (\r\n <div key={item.title}>\r\n <div className=\"min-w-max flex relative flex-col justify-center align-middle\">\r\n <div className=\"capitalize pt-3 pb-1 px-1.5 leading-normal font-medium text-xs text-muted-foreground\">\r\n {item.title}\r\n </div>\r\n <div className=\"flex flex-col\">\r\n <Button\r\n variant={isParagraph ? \"secondary\" : \"ghost\"}\r\n className=\"w-full justify-start\"\r\n onClick={() => editor.chain().focus().setParagraph().run()}\r\n >\r\n <Type /> {t.blocks.text.title}\r\n </Button>\r\n {headings.map((head) => (\r\n <Heading head={head} editor={editor} />\r\n ))}\r\n <Button\r\n variant={isBulletList ? \"secondary\" : \"ghost\"}\r\n className=\"w-full justify-start\"\r\n onClick={() =>\r\n editor.chain().focus().toggleBulletList().run()\r\n }\r\n >\r\n <List /> {t.blocks.bulletList.title}\r\n </Button>\r\n <Button\r\n variant={isOrderedList ? \"secondary\" : \"ghost\"}\r\n className=\"w-full justify-start\"\r\n onClick={() =>\r\n editor.chain().focus().toggleOrderedList().run()\r\n }\r\n >\r\n <ListOrdered /> {t.blocks.orderedList.title}\r\n </Button>\r\n <Button\r\n variant={isTaskList ? \"secondary\" : \"ghost\"}\r\n className=\"w-full justify-start\"\r\n onClick={() =>\r\n editor.chain().focus().toggleTaskList().run()\r\n }\r\n >\r\n <ListTodo /> {t.blocks.taskList.title}\r\n </Button>\r\n\r\n <Button\r\n disabled={isTaskList}\r\n variant={isBlockquote ? \"secondary\" : \"ghost\"}\r\n className=\"w-full justify-start\"\r\n onClick={() =>\r\n editor.chain().focus().toggleBlockquote().run()\r\n }\r\n >\r\n <TextQuote /> {t.blocks.blockquote.title}\r\n </Button>\r\n <Button\r\n disabled={isTaskList}\r\n variant={isCodeBlock ? \"secondary\" : \"ghost\"}\r\n className=\"w-full justify-start\"\r\n onClick={() =>\r\n editor.chain().focus().toggleCodeBlock().run()\r\n }\r\n >\r\n <CodeXmlIcon /> {t.blocks.codeBlock.title}\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n ))}\r\n </ScrollArea>\r\n </PopoverContent>\r\n </Popover>\r\n );\r\n};\r\n\r\nexport default TextTurnInto;\r\n\r\nconst Heading = ({ head, editor }: { head: Headings; editor: Editor }) => {\r\n const { isActive } = useEditorState({\r\n editor,\r\n selector: (ctx) => ({\r\n isActive: ctx.editor.isActive(\"heading\", { level: head.level }),\r\n }),\r\n });\r\n return (\r\n <Button\r\n variant={isActive ? \"secondary\" : \"ghost\"}\r\n className=\"w-full justify-start\"\r\n onClick={() =>\r\n editor.chain().focus().toggleHeading({ level: head.level }).run()\r\n }\r\n >\r\n <head.icon /> {head.title}\r\n </Button>\r\n );\r\n};\r\n","import { Editor } from \"@tiptap/core\";\r\nimport { BubbleMenu } from \"@tiptap/react/menus\";\r\nimport {\r\n Bold,\r\n Code,\r\n Italic,\r\n Strikethrough,\r\n Underline,\r\n} from \"lucide-react\";\r\n\r\nimport ToggleButton from \"../ToggleButton\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\nimport { Separator } from \"../ui/separator\";\r\nimport { Button } from \"../ui/button\";\r\nimport TooltipWrapper from \"../TooltipWrapper\";\r\nimport PickLink from \"./PickLink\";\r\nimport BubbleMoreOption from \"./BubbleMoreOption\";\r\nimport ColorDropdown from \"../color/ColorDropdown\";\r\nimport TextTurnInto from \"./TextTurnInto\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\nconst MyBubbleMenu = ({ editor }: { editor: Editor | null }) => {\r\n const t = useMessages();\r\n if (!editor) {\r\n return;\r\n }\r\n\r\n const { isBold, isItalic, isStrikethrough, isUnderline, isCode } =\r\n useEditorState({\r\n editor,\r\n selector: (ctx) => ({\r\n isBold: ctx.editor.isActive(\"bold\"),\r\n isItalic: ctx.editor.isActive(\"italic\"),\r\n isStrikethrough: ctx.editor.isActive(\"strike\"),\r\n isUnderline: ctx.editor.isActive(\"underline\"),\r\n isCode: ctx.editor.isActive(\"code\"),\r\n }),\r\n });\r\n\r\n\r\n\r\n\r\n return (\r\n <BubbleMenu\r\n editor={editor}\r\n pluginKey=\"textMenu\"\r\n className=\"bg-card not-prose border shadow-xl rounded-xl px-1 flex items-center gap-1 py-1\"\r\n shouldShow={({ editor, view, state, from, to }) => {\r\n if (editor.isActive(\"image\") || editor.isActive(\"imageUpload\") || editor.isActive(\"table\")) {\r\n return false;\r\n }\r\n return view.hasFocus() && !state.selection.empty;\r\n }}\r\n >\r\n <TextTurnInto editor={editor} />\r\n <Separator orientation=\"vertical\" />\r\n <div className=\" flex items-center gap-1 \">\r\n <ToggleButton\r\n tooltip={t.marks.bold}\r\n icon={Bold}\r\n isActive={isBold}\r\n onClick={() => editor.chain().focus().toggleBold().run()}\r\n disabled={isCode}\r\n />\r\n <ToggleButton\r\n tooltip={t.marks.italic}\r\n icon={Italic}\r\n isActive={isItalic}\r\n onClick={() => editor.chain().focus().toggleItalic().run()}\r\n disabled={isCode}\r\n />\r\n\r\n <ToggleButton\r\n tooltip={t.marks.underline}\r\n icon={Underline}\r\n isActive={isUnderline}\r\n onClick={() => editor.chain().focus().toggleUnderline().run()}\r\n disabled={isCode}\r\n />\r\n <ToggleButton\r\n tooltip=\"Strikethrough\"\r\n icon={Strikethrough}\r\n isActive={isStrikethrough}\r\n onClick={() => editor.chain().focus().toggleStrike().run()}\r\n disabled={isCode}\r\n />\r\n <ToggleButton\r\n tooltip={t.marks.code}\r\n icon={Code}\r\n isActive={isCode}\r\n onClick={() => editor.chain().focus().toggleCode().run()}\r\n />\r\n </div>\r\n <Separator orientation=\"vertical\" />\r\n <div className=\" flex gap-0.5\">\r\n <PickLink editor={editor} />\r\n <ColorDropdown editor={editor} />\r\n </div>\r\n <Separator orientation=\"vertical\" />\r\n <BubbleMoreOption editor={editor} />\r\n </BubbleMenu>\r\n );\r\n};\r\n\r\nexport default MyBubbleMenu;\r\n","import {\r\n arrow,\r\n autoPlacement,\r\n computePosition,\r\n flip,\r\n hide,\r\n inline,\r\n Middleware,\r\n offset,\r\n shift,\r\n size,\r\n} from \"@floating-ui/dom\";\r\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\r\nimport {\r\n CellSelection,\r\n columnResizingPluginKey,\r\n tableEditingKey\r\n} from \"@tiptap/pm/tables\";\r\nimport { EditorView } from \"@tiptap/pm/view\";\r\nimport { Editor, findParentNodeClosestToPos } from \"@tiptap/react\";\r\n\r\ninterface TableMenuHandlePluginState {\r\n openedMenu: boolean;\r\n}\r\n\r\nexport interface FloatingUIOptionsProps {\r\n strategy?: \"absolute\" | \"fixed\";\r\n placement?:\r\n | \"top\"\r\n | \"right\"\r\n | \"bottom\"\r\n | \"left\"\r\n | \"top-start\"\r\n | \"top-end\"\r\n | \"right-start\"\r\n | \"right-end\"\r\n | \"bottom-start\"\r\n | \"bottom-end\"\r\n | \"left-start\"\r\n | \"left-end\";\r\n offset?: Parameters<typeof offset>[0] | boolean;\r\n flip?: Parameters<typeof flip>[0] | boolean;\r\n shift?: Parameters<typeof shift>[0] | boolean;\r\n arrow?: Parameters<typeof arrow>[0] | false;\r\n size?: Parameters<typeof size>[0] | boolean;\r\n autoPlacement?: Parameters<typeof autoPlacement>[0] | boolean;\r\n hide?: Parameters<typeof hide>[0] | boolean;\r\n inline?: Parameters<typeof inline>[0] | boolean;\r\n\r\n onShow?: () => void;\r\n onHide?: () => void;\r\n onUpdate?: () => void;\r\n onDestroy?: () => void;\r\n}\r\n\r\nexport interface TableMenuHandlePluginProps {\r\n pluginKey: PluginKey<TableMenuHandlePluginState>;\r\n editor: Editor;\r\n element: HTMLElement;\r\n menuType: \"row\" | \"column\";\r\n options?: FloatingUIOptionsProps;\r\n}\r\n\r\nexport const rowMenuPluginKey = new PluginKey<TableMenuHandlePluginState>(\r\n \"table-row-menu-handle\"\r\n);\r\nexport const columnMenuPluginKey = new PluginKey<TableMenuHandlePluginState>(\r\n \"table-column-menu-handle\"\r\n);\r\n\r\nexport const TableMenuHandlePlugin = ({\r\n pluginKey,\r\n editor,\r\n element,\r\n menuType,\r\n options,\r\n}: TableMenuHandlePluginProps) => {\r\n const floatingUIOptions: NonNullable<FloatingUIOptionsProps> = options ?? {\r\n strategy: \"absolute\",\r\n placement: \"top\",\r\n offset: 0,\r\n flip: {},\r\n shift: {},\r\n arrow: false,\r\n size: false,\r\n autoPlacement: false,\r\n hide: false,\r\n inline: false,\r\n };\r\n\r\n const isRow = menuType === \"row\";\r\n const middlewares: Middleware[] = [];\r\n\r\n let ticking = false;\r\n let isVisible = false;\r\n let currentTablePos: number | undefined;\r\n let currentCellPos: number | undefined;\r\n\r\n const getMiddlewares = () => {\r\n if (middlewares.length > 0) {\r\n return middlewares;\r\n }\r\n\r\n if (floatingUIOptions.flip) {\r\n middlewares.push(\r\n flip(\r\n typeof floatingUIOptions.flip !== \"boolean\"\r\n ? floatingUIOptions.flip\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.shift) {\r\n middlewares.push(\r\n shift(\r\n typeof floatingUIOptions.shift !== \"boolean\"\r\n ? floatingUIOptions.shift\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.offset) {\r\n middlewares.push(\r\n offset(\r\n typeof floatingUIOptions.offset !== \"boolean\"\r\n ? floatingUIOptions.offset\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.arrow) {\r\n middlewares.push(arrow(floatingUIOptions.arrow));\r\n }\r\n\r\n if (floatingUIOptions.size) {\r\n middlewares.push(\r\n size(\r\n typeof floatingUIOptions.size !== \"boolean\"\r\n ? floatingUIOptions.size\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.autoPlacement) {\r\n middlewares.push(\r\n autoPlacement(\r\n typeof floatingUIOptions.autoPlacement !== \"boolean\"\r\n ? floatingUIOptions.autoPlacement\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.hide) {\r\n middlewares.push(\r\n hide(\r\n typeof floatingUIOptions.hide !== \"boolean\"\r\n ? floatingUIOptions.hide\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.inline) {\r\n middlewares.push(\r\n inline(\r\n typeof floatingUIOptions.inline !== \"boolean\"\r\n ? floatingUIOptions.inline\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n return middlewares;\r\n };\r\n\r\n const selectCells = () => {\r\n if (currentCellPos === undefined) {\r\n return;\r\n }\r\n\r\n const cellPos = currentCellPos;\r\n editor\r\n .chain()\r\n .command(({ tr, state }) => {\r\n const resolvedPos = state.doc.resolve(cellPos);\r\n const selectFn = isRow\r\n ? CellSelection.rowSelection\r\n : CellSelection.colSelection;\r\n const selection = selectFn(resolvedPos);\r\n tr.setSelection(selection);\r\n return true;\r\n })\r\n .run();\r\n };\r\n\r\n const showMenu = (_view: EditorView, root: HTMLElement | null) => {\r\n if (isVisible) {\r\n return;\r\n }\r\n\r\n element.style.visibility = \"visible\";\r\n element.style.opacity = \"1\";\r\n // attach to editor's parent element\r\n root?.appendChild(element);\r\n // view.dom.parentElement?.appendChild(element);\r\n\r\n floatingUIOptions.onShow?.();\r\n\r\n isVisible = true;\r\n };\r\n\r\n const hideMenu = () => {\r\n if (!isVisible) {\r\n return;\r\n }\r\n\r\n const pluginState = pluginKey.getState(editor.view.state);\r\n if (pluginState?.openedMenu) {\r\n return;\r\n }\r\n\r\n element.style.visibility = \"hidden\";\r\n element.style.opacity = \"0\";\r\n element.remove();\r\n\r\n floatingUIOptions.onHide?.();\r\n\r\n isVisible = false;\r\n currentCellPos = undefined;\r\n currentTablePos = undefined;\r\n // console.log(\"hide menu: \", pluginKey);\r\n };\r\n\r\n const updatePosition = (view: EditorView, rect: DOMRect) => {\r\n const pluginState = pluginKey.getState(view.state);\r\n if (pluginState?.openedMenu) {\r\n return;\r\n }\r\n\r\n const virtualElement = {\r\n getBoundingClientRect: () => rect,\r\n getClientRects: () => [rect],\r\n };\r\n\r\n computePosition(virtualElement, element, {\r\n placement: floatingUIOptions.placement,\r\n strategy: floatingUIOptions.strategy,\r\n middleware: getMiddlewares(),\r\n }).then(({ x, y, strategy }) => {\r\n if (isRow) {\r\n element.style.height = `${rect.height}px`;\r\n } else {\r\n element.style.width = `${rect.width}px`;\r\n }\r\n element.style.position = strategy;\r\n element.style.left = `${x}px`;\r\n element.style.top = `${y}px`;\r\n\r\n if (isVisible) {\r\n floatingUIOptions.onUpdate?.();\r\n }\r\n });\r\n };\r\n\r\n const handleMouseMove = (view: EditorView, evt: MouseEvent) => {\r\n const target = evt.target as Element;\r\n if (!target) {\r\n return;\r\n }\r\n\r\n if (columnResizingPluginKey.getState(view.state)?.dragging) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n if (tableEditingKey.getState(view.state) !== null) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n if (isRow && columnMenuPluginKey.getState(view.state)?.openedMenu) {\r\n hideMenu();\r\n return;\r\n } else if (rowMenuPluginKey.getState(view.state)?.openedMenu) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n const pluginState = pluginKey.getState(view.state);\r\n if (pluginState?.openedMenu) {\r\n return;\r\n }\r\n\r\n const pos = view.posAtDOM(target, 0);\r\n\r\n if (pos === undefined) {\r\n return;\r\n }\r\n\r\n let cellNodePos: number | undefined = undefined;\r\n let tableNodePos: number | undefined = undefined;\r\n\r\n view.state.doc.nodesBetween(pos, pos, (node, p) => {\r\n if (node.type.name === \"tableHeader\") {\r\n cellNodePos = p;\r\n return false;\r\n }\r\n if (node.type.name === \"tableCell\") {\r\n cellNodePos = p;\r\n return false;\r\n }\r\n if (node.type.name === \"table\") {\r\n tableNodePos = p;\r\n }\r\n return true;\r\n });\r\n\r\n if (tableNodePos === undefined) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n if (currentTablePos !== tableNodePos) {\r\n hideMenu();\r\n }\r\n\r\n currentTablePos = tableNodePos;\r\n\r\n const tableRoot = view.nodeDOM(tableNodePos);\r\n if (!tableRoot || !(tableRoot instanceof HTMLDivElement)) {\r\n hideMenu();\r\n return;\r\n }\r\n // console.log(TableMap.get(view.state.doc.nodeAt(tableNodePos)!));\r\n\r\n const table = tableRoot.querySelector(\"table\");\r\n if (!table) {\r\n return;\r\n }\r\n\r\n const tableRect = table.getBoundingClientRect();\r\n const offset = 16;\r\n\r\n if (evt.x > tableRect.right + offset) {\r\n hideMenu();\r\n return;\r\n }\r\n if (evt.x < tableRect.left - offset) {\r\n hideMenu();\r\n return;\r\n }\r\n if (evt.y < tableRect.top - offset) {\r\n hideMenu();\r\n return;\r\n }\r\n if (evt.y > tableRect.bottom + offset) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n if (cellNodePos === undefined) {\r\n return;\r\n }\r\n\r\n if (currentCellPos === cellNodePos) {\r\n return;\r\n }\r\n\r\n const tableCell = view.nodeDOM(cellNodePos);\r\n if (!tableCell || !(tableCell instanceof HTMLTableCellElement)) {\r\n return;\r\n }\r\n\r\n const tableCellRect = tableCell.getBoundingClientRect();\r\n\r\n currentCellPos = cellNodePos;\r\n\r\n if (isRow) {\r\n tableCellRect.x = tableRect.x;\r\n } else {\r\n tableCellRect.y = tableRect.y;\r\n }\r\n\r\n updatePosition(view, tableCellRect);\r\n\r\n if (isVisible) {\r\n return;\r\n }\r\n\r\n showMenu(view, tableRoot.querySelector(\".table-controls\"));\r\n };\r\n\r\n return new Plugin<TableMenuHandlePluginState>({\r\n key: pluginKey,\r\n state: {\r\n init: () => {\r\n return {\r\n openedMenu: false,\r\n };\r\n },\r\n\r\n apply: (tr, value) => {\r\n const meta = tr.getMeta(pluginKey);\r\n // console.log(meta);\r\n if (meta) {\r\n return {\r\n openedMenu: meta.openedMenu,\r\n };\r\n }\r\n return value;\r\n },\r\n },\r\n view: () => {\r\n return {\r\n update: (view, prevState) => {\r\n const prevPluginState = pluginKey.getState(prevState);\r\n const pluginState = pluginKey.getState(view.state);\r\n\r\n const pluginStateChanged = prevPluginState !== pluginState;\r\n if (pluginStateChanged && pluginState?.openedMenu) {\r\n selectCells();\r\n }\r\n\r\n if (isRow && columnMenuPluginKey.getState(view.state)?.openedMenu) {\r\n hideMenu();\r\n return;\r\n } else if (rowMenuPluginKey.getState(view.state)?.openedMenu) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n const selectionChanged = !prevState?.selection.eq(\r\n view.state.selection\r\n );\r\n const docChanged = !prevState?.doc.eq(view.state.doc);\r\n\r\n const { composing } = view;\r\n\r\n const isSame =\r\n !selectionChanged && !docChanged && !pluginStateChanged;\r\n\r\n if (composing || isSame) {\r\n return;\r\n }\r\n\r\n if (!editor.isActive(\"table\")) {\r\n return;\r\n }\r\n\r\n if (currentCellPos === undefined) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n const cellNode = view.state.doc.nodeAt(currentCellPos);\r\n const tableNode = findParentNodeClosestToPos(\r\n view.state.doc.resolve(currentCellPos),\r\n (n) => {\r\n return n.type.name === \"table\";\r\n }\r\n );\r\n\r\n if (!tableNode || !cellNode) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n const tableCell = view.nodeDOM(currentCellPos);\r\n const tableRoot = view.nodeDOM(tableNode.pos);\r\n\r\n if (!tableRoot || !(tableRoot instanceof HTMLDivElement)) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n if (!tableCell || !(tableCell instanceof HTMLTableCellElement)) {\r\n hideMenu();\r\n return;\r\n }\r\n\r\n const table = tableRoot.querySelector(\"table\");\r\n if (!table) {\r\n return;\r\n }\r\n\r\n const tableRect = table.getBoundingClientRect();\r\n const tableCellRect = tableCell.getBoundingClientRect();\r\n\r\n if (isRow) {\r\n tableCellRect.x = tableRect.x;\r\n } else {\r\n tableCellRect.y = tableRect.y;\r\n }\r\n updatePosition(view, tableCellRect);\r\n },\r\n destroy: () => {\r\n hideMenu();\r\n\r\n floatingUIOptions.onDestroy?.();\r\n },\r\n };\r\n },\r\n props: {\r\n handleDOMEvents: {\r\n mousemove: (view, evt) => {\r\n if (!ticking) {\r\n window.requestAnimationFrame(() => {\r\n handleMouseMove(view, evt);\r\n ticking = false;\r\n });\r\n ticking = true;\r\n }\r\n },\r\n mousedown: handleMouseMove,\r\n // mouseleave: hideMenu,\r\n },\r\n },\r\n });\r\n};\r\n","import { useEffect, useRef } from \"react\";\r\nimport { createPortal } from \"react-dom\";\r\nimport {\r\n TableMenuHandlePlugin,\r\n TableMenuHandlePluginProps,\r\n} from \"./plugins/table-menu-handle-plugin\";\r\n\r\nexport interface TableMenuHandleProps {\r\n ref?: React.ForwardedRef<HTMLDivElement>;\r\n children: React.ReactNode;\r\n pluginProps: Omit<TableMenuHandlePluginProps, \"element\">;\r\n}\r\n\r\nexport const TableMenuHandle = ({\r\n ref,\r\n children,\r\n pluginProps,\r\n}: TableMenuHandleProps) => {\r\n const rootElementRef = useRef(document.createElement(\"div\"));\r\n\r\n useEffect(() => {\r\n if (typeof ref === \"function\") {\r\n ref(rootElementRef.current);\r\n } else if (ref) {\r\n ref.current = rootElementRef.current;\r\n }\r\n }, [ref]);\r\n\r\n useEffect(() => {\r\n const editor = pluginProps.editor;\r\n const rootElement = rootElementRef.current;\r\n\r\n rootElement.style.visibility = \"hidden\";\r\n rootElement.style.position = \"absolute\";\r\n\r\n if (editor.isDestroyed) {\r\n return;\r\n }\r\n\r\n const plugin = TableMenuHandlePlugin({\r\n ...pluginProps,\r\n element: rootElement,\r\n });\r\n\r\n editor.registerPlugin(plugin);\r\n\r\n return () => {\r\n editor.unregisterPlugin(pluginProps.pluginKey);\r\n window.requestAnimationFrame(() => {\r\n if (rootElement.parentNode) {\r\n rootElement.parentNode.removeChild(rootElement);\r\n }\r\n });\r\n };\r\n }, [pluginProps]);\r\n\r\n return createPortal(<>{children}</>, rootElementRef.current);\r\n};\r\n","import { EllipsisIcon, EqualIcon, PaintBucket, SquareX } from \"lucide-react\";\r\nimport { useState } from \"react\";\r\nimport {\r\n DropdownMenu,\r\n DropdownMenuContent,\r\n DropdownMenuGroup,\r\n DropdownMenuItem,\r\n DropdownMenuSeparator,\r\n DropdownMenuTrigger,\r\n} from \"~/components/ui/dropdown-menu\";\r\nimport { cn } from \"~/lib/utils\";\r\nimport { Editor } from \"@tiptap/core\";\r\nimport { useEditorState } from \"@tiptap/react\";\r\nimport { TextSelection } from \"@tiptap/pm/state\";\r\nimport { CellSelection, deleteCellSelection } from \"@tiptap/pm/tables\";\r\nimport { AlignmentDropdown } from \"./AlignmentContent\";\r\nimport { ITEM_CLASSNAME } from \"./TableMenuPopover\";\r\nimport { ColorsDropdwon } from \"./Colors\";\r\nimport { TableMergeIcon } from \"~/components/icons\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\ninterface CellMenusState {\r\n canMergeCell: boolean;\r\n canSplitCell: boolean;\r\n canClearContents: boolean;\r\n}\r\n\r\nexport const CellMenuPopover = ({ editor }: { editor: Editor }) => {\r\n const t = useMessages();\r\n const [opened, setOpened] = useState(false);\r\n const { canMergeCell, canSplitCell, canClearContents } =\r\n useEditorState<CellMenusState>({\r\n editor: editor,\r\n equalityFn: (a, b) => {\r\n return (\r\n a.canMergeCell === b?.canMergeCell &&\r\n a.canSplitCell === b.canSplitCell &&\r\n a.canClearContents === b.canClearContents\r\n );\r\n },\r\n selector: (instance) => {\r\n const editor = instance.editor;\r\n const { selection } = editor.state;\r\n const { from, to, ranges } = selection;\r\n if (!instance.editor.isActive(\"table\")) {\r\n return {\r\n canMergeCell: false,\r\n canSplitCell: false,\r\n canClearContents: false,\r\n };\r\n }\r\n\r\n let hasSpannedCell = false;\r\n let cellSelectionCount = 0;\r\n let selectionContentSize = 0;\r\n\r\n if (selection instanceof TextSelection) {\r\n editor.state.doc.nodesBetween(from, to, (node, pos) => {\r\n const nodeName = node.type.name;\r\n if (nodeName === \"tableHeader\" || nodeName === \"tableCell\") {\r\n const cell = editor.view.nodeDOM(pos) as HTMLTableCellElement;\r\n hasSpannedCell = cell.colSpan > 1 || cell.rowSpan > 1;\r\n return false;\r\n }\r\n return true;\r\n });\r\n }\r\n\r\n if (selection instanceof CellSelection) {\r\n cellSelectionCount = selection.ranges.length;\r\n for (const range of ranges) {\r\n const { $from, $to } = range;\r\n editor.state.doc.nodesBetween($from.pos, $to.pos, (node) => {\r\n if (node.isTextblock) {\r\n selectionContentSize += node.content.size;\r\n }\r\n return true;\r\n });\r\n }\r\n }\r\n\r\n return {\r\n canMergeCell: cellSelectionCount > 1,\r\n canSplitCell: hasSpannedCell,\r\n canClearContents: selectionContentSize > 0,\r\n };\r\n },\r\n });\r\n\r\n return (\r\n <DropdownMenu open={opened} onOpenChange={setOpened}>\r\n <DropdownMenuTrigger\r\n className={cn(\r\n \"absolute flex items-center justify-center top-1/2 -translate-y-1/2 hover:-right-2.25 bg-primary size-2 hover:size-4 rounded-full cursor-pointer pointer-events-auto\",\r\n {\r\n \"size-4 -right-2.25\": opened,\r\n \"-right-1.25\": !opened,\r\n },\r\n )}\r\n asChild\r\n >\r\n <button\r\n onPointerDown={(evt) => {\r\n evt.preventDefault();\r\n setOpened(true);\r\n }}\r\n >\r\n <EqualIcon\r\n className={cn(\r\n \"size-3.5 text-primary-foreground opacity-0 hover:opacity-100\",\r\n {\r\n \"opacity-100\": opened,\r\n },\r\n )}\r\n />\r\n </button>\r\n </DropdownMenuTrigger>\r\n <DropdownMenuContent\r\n className=\"flex max-h-80 w-55 flex-col overflow-hidden overflow-y-auto shadow-xl\"\r\n align=\"start\"\r\n side=\"bottom\"\r\n >\r\n <DropdownMenuGroup>\r\n <DropdownMenuItem\r\n hidden={!canMergeCell}\r\n onClick={() => {\r\n editor.chain().focus().mergeCells().run();\r\n }}\r\n >\r\n <TableMergeIcon /> {t.table.mergeCells}\r\n </DropdownMenuItem>\r\n <DropdownMenuItem\r\n hidden={!canSplitCell}\r\n onClick={() => {\r\n editor.chain().focus().splitCell().run();\r\n }}\r\n >\r\n <TableMergeIcon /> {t.table.splitCell}\r\n </DropdownMenuItem>\r\n </DropdownMenuGroup>\r\n {(canMergeCell || canSplitCell) && <DropdownMenuSeparator />}\r\n <DropdownMenuGroup>\r\n <ColorsDropdwon editor={editor} />\r\n <AlignmentDropdown editor={editor} />\r\n {/* <DropdownMenuSub>\r\n <DropdownMenuPortal>\r\n <DropdownMenuSubContent>\r\n <DropdownMenuItem\r\n onClick={() => {\r\n editor\r\n .chain()\r\n .focus()\r\n .setCellAttribute(\"verticalAlign\", \"top\")\r\n .run();\r\n }}\r\n >\r\n {t.table.alignTop}\r\n </DropdownMenuItem>\r\n <DropdownMenuItem\r\n onClick={() => {\r\n editor\r\n .chain()\r\n .focus()\r\n .setCellAttribute(\"verticalAlign\", \"middle\")\r\n .run();\r\n }}\r\n >\r\n {t.table.alignMiddle}\r\n </DropdownMenuItem>\r\n <DropdownMenuItem\r\n onClick={() => {\r\n editor\r\n .chain()\r\n .focus()\r\n .setCellAttribute(\"verticalAlign\", \"bottom\")\r\n .run();\r\n }}\r\n >\r\n {t.table.alignBottom}\r\n </DropdownMenuItem>\r\n </DropdownMenuSubContent>\r\n </DropdownMenuPortal>\r\n </DropdownMenuSub> */}\r\n </DropdownMenuGroup>\r\n {canClearContents && <DropdownMenuSeparator />}\r\n {canClearContents && (\r\n <DropdownMenuItem\r\n className={ITEM_CLASSNAME}\r\n onClick={() => {\r\n editor\r\n .chain()\r\n .focus()\r\n .command(({ state, dispatch }) => {\r\n return deleteCellSelection(state, dispatch);\r\n })\r\n .run();\r\n }}\r\n >\r\n <SquareX /> {t.table.clearColumnContents}\r\n </DropdownMenuItem>\r\n )}\r\n </DropdownMenuContent>\r\n </DropdownMenu>\r\n );\r\n};\r\n","import {\r\n arrow,\r\n autoPlacement,\r\n computePosition,\r\n flip,\r\n hide,\r\n inline,\r\n Middleware,\r\n offset,\r\n shift,\r\n size,\r\n} from \"@floating-ui/dom\";\r\nimport { Plugin, PluginKey, TextSelection } from \"@tiptap/pm/state\";\r\nimport {\r\n CellSelection,\r\n columnResizingPluginKey\r\n} from \"@tiptap/pm/tables\";\r\nimport { EditorView } from \"@tiptap/pm/view\";\r\nimport { Editor } from \"@tiptap/react\";\r\nimport { FloatingUIOptionsProps } from \"./table-menu-handle-plugin\";\r\n\r\ninterface SelectionRect {\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface TableSelectionOverlayPluginProps {\r\n pluginKey: PluginKey;\r\n editor: Editor;\r\n element: HTMLElement;\r\n options?: FloatingUIOptionsProps;\r\n}\r\n\r\nexport const TableSelectionOverlayPlugin = ({\r\n pluginKey,\r\n editor,\r\n element,\r\n options,\r\n}: TableSelectionOverlayPluginProps) => {\r\n const floatingUIOptions: NonNullable<FloatingUIOptionsProps> = options ?? {\r\n strategy: \"absolute\",\r\n placement: \"bottom-start\",\r\n offset: ({ rects }) => {\r\n return {\r\n mainAxis: -rects.reference.height,\r\n };\r\n },\r\n flip: false,\r\n shift: false,\r\n arrow: false,\r\n size: false,\r\n autoPlacement: false,\r\n hide: false,\r\n inline: false,\r\n };\r\n const middlewares: Middleware[] = [];\r\n\r\n let isVisible = false;\r\n let currentTablePos: number | undefined;\r\n\r\n const getMiddlewares = () => {\r\n if (middlewares.length > 0) {\r\n return middlewares;\r\n }\r\n\r\n if (floatingUIOptions.flip) {\r\n middlewares.push(\r\n flip(\r\n typeof floatingUIOptions.flip !== \"boolean\"\r\n ? floatingUIOptions.flip\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.shift) {\r\n middlewares.push(\r\n shift(\r\n typeof floatingUIOptions.shift !== \"boolean\"\r\n ? floatingUIOptions.shift\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.offset) {\r\n middlewares.push(\r\n offset(\r\n typeof floatingUIOptions.offset !== \"boolean\"\r\n ? floatingUIOptions.offset\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.arrow) {\r\n middlewares.push(arrow(floatingUIOptions.arrow));\r\n }\r\n\r\n if (floatingUIOptions.size) {\r\n middlewares.push(\r\n size(\r\n typeof floatingUIOptions.size !== \"boolean\"\r\n ? floatingUIOptions.size\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.autoPlacement) {\r\n middlewares.push(\r\n autoPlacement(\r\n typeof floatingUIOptions.autoPlacement !== \"boolean\"\r\n ? floatingUIOptions.autoPlacement\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.hide) {\r\n middlewares.push(\r\n hide(\r\n typeof floatingUIOptions.hide !== \"boolean\"\r\n ? floatingUIOptions.hide\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n if (floatingUIOptions.inline) {\r\n middlewares.push(\r\n inline(\r\n typeof floatingUIOptions.inline !== \"boolean\"\r\n ? floatingUIOptions.inline\r\n : undefined\r\n )\r\n );\r\n }\r\n\r\n return middlewares;\r\n };\r\n\r\n const updatePosition = (view: EditorView) => {\r\n const { domRect, selectionRect } = findRects(view);\r\n\r\n if (!domRect || !selectionRect) {\r\n hideOverlay();\r\n return;\r\n }\r\n\r\n element.style.width = `${selectionRect.width}px`;\r\n element.style.height = `${selectionRect.height}px`;\r\n\r\n const virtualElement = {\r\n getBoundingClientRect: () => domRect,\r\n getClientRects: () => [domRect],\r\n };\r\n\r\n computePosition(virtualElement, element, {\r\n placement: floatingUIOptions.placement,\r\n strategy: floatingUIOptions.strategy,\r\n middleware: getMiddlewares(),\r\n }).then(({ x, y, strategy }) => {\r\n element.style.position = strategy;\r\n element.style.left = `${x}px`;\r\n element.style.top = `${y}px`;\r\n if (isVisible) {\r\n floatingUIOptions.onUpdate?.();\r\n }\r\n });\r\n\r\n // console.log(\"update position: \", selectionRect);\r\n };\r\n\r\n const showOverlay = (_view: EditorView, root: HTMLElement | null) => {\r\n if (isVisible) {\r\n return;\r\n }\r\n\r\n element.style.visibility = \"visible\";\r\n element.style.opacity = \"1\";\r\n // attach to editor's parent element\r\n root?.appendChild(element);\r\n // view.dom.parentElement?.appendChild(element);\r\n\r\n isVisible = true;\r\n };\r\n\r\n const hideOverlay = () => {\r\n if (!isVisible) {\r\n return;\r\n }\r\n\r\n element.style.visibility = \"hidden\";\r\n element.style.opacity = \"0\";\r\n element.remove();\r\n\r\n isVisible = false;\r\n currentTablePos = undefined;\r\n };\r\n\r\n const handleResize = () => {\r\n if (!editor.isActive(\"table\")) {\r\n return;\r\n }\r\n\r\n updatePosition(editor.view);\r\n };\r\n\r\n const findRects = (view: EditorView) => {\r\n let selectionRect: SelectionRect | undefined;\r\n let domRect: DOMRect | undefined;\r\n\r\n const { selection } = view.state;\r\n if (selection instanceof CellSelection) {\r\n let top: number | undefined;\r\n let left: number | undefined;\r\n let right: number | undefined;\r\n let bottom: number | undefined;\r\n selection.forEachCell((_node, pos) => {\r\n const cell = view.nodeDOM(pos) as HTMLTableCellElement;\r\n const rect = cell.getBoundingClientRect();\r\n if (!domRect) {\r\n domRect = rect;\r\n }\r\n\r\n top = Math.min(top ?? rect.top, rect.top);\r\n left = Math.min(left ?? rect.left, rect.left);\r\n right = Math.max(right ?? rect.right, rect.right);\r\n bottom = Math.max(bottom ?? rect.bottom, rect.bottom);\r\n\r\n selectionRect = {\r\n width: right - left,\r\n height: bottom - top,\r\n };\r\n\r\n if (rect.left < domRect.left && rect.top < domRect.top) {\r\n domRect = rect;\r\n }\r\n });\r\n }\r\n\r\n if (selection instanceof TextSelection) {\r\n view.state.doc.nodesBetween(selection.from, selection.to, (node, pos) => {\r\n const nodeName = node.type.name;\r\n if (nodeName === \"tableHeader\" || nodeName === \"tableCell\") {\r\n const cell = view.nodeDOM(pos) as HTMLTableCellElement;\r\n const rect = cell.getBoundingClientRect();\r\n domRect = rect;\r\n selectionRect = {\r\n width: rect.width,\r\n height: rect.height,\r\n };\r\n return false;\r\n }\r\n return true;\r\n });\r\n }\r\n\r\n // const tableRect = selectedRect(view.state);\r\n\r\n return {\r\n domRect,\r\n selectionRect,\r\n };\r\n };\r\n\r\n return new Plugin({\r\n key: pluginKey,\r\n view: () => {\r\n window.addEventListener(\"resize\", handleResize);\r\n return {\r\n update: (view, prevState) => {\r\n const selectionChanged = !prevState?.selection.eq(\r\n view.state.selection\r\n );\r\n const docChanged = !prevState?.doc.eq(view.state.doc);\r\n\r\n const { composing } = view;\r\n\r\n const isSame = !selectionChanged && !docChanged;\r\n\r\n if (composing || isSame) {\r\n return;\r\n }\r\n\r\n if (!editor.isActive(\"table\")) {\r\n hideOverlay();\r\n return;\r\n }\r\n\r\n updatePosition(view);\r\n\r\n const { ranges } = view.state.selection;\r\n const from = Math.min(...ranges.map((range) => range.$from.pos));\r\n const to = Math.max(...ranges.map((range) => range.$to.pos));\r\n\r\n let tableNodePos: number | undefined = undefined;\r\n\r\n editor.state.doc.nodesBetween(from, to, (node, p) => {\r\n if (node.type.name === \"table\") {\r\n tableNodePos = p;\r\n return false;\r\n }\r\n return false;\r\n });\r\n\r\n if (tableNodePos === undefined) {\r\n hideOverlay();\r\n return;\r\n }\r\n\r\n if (currentTablePos !== tableNodePos) {\r\n hideOverlay();\r\n }\r\n\r\n currentTablePos = tableNodePos;\r\n\r\n if (isVisible) {\r\n return;\r\n }\r\n\r\n const table = view.nodeDOM(tableNodePos) as HTMLDivElement;\r\n if (!table) {\r\n hideOverlay();\r\n return;\r\n }\r\n\r\n showOverlay(view, table.querySelector(\".table-selection-container\"));\r\n },\r\n destroy: () => {\r\n window.removeEventListener(\"resize\", handleResize);\r\n hideOverlay();\r\n floatingUIOptions.onDestroy?.();\r\n },\r\n };\r\n },\r\n props: {\r\n handleDOMEvents: {\r\n mousemove: (view) => {\r\n if (!columnResizingPluginKey.getState(view.state)?.dragging) {\r\n return;\r\n }\r\n\r\n updatePosition(view);\r\n },\r\n },\r\n },\r\n });\r\n};\r\n","import { useEffect, useRef } from \"react\";\r\nimport { createPortal } from \"react-dom\";\r\nimport { TableSelectionOverlayPlugin, TableSelectionOverlayPluginProps } from \"./plugins/table-selection-overlay\";\r\nimport { cn } from \"~/lib/utils\";\r\n\r\n\r\nexport interface TableSelectionOverlayProps {\r\n ref?: React.ForwardedRef<HTMLDivElement>;\r\n pluginProps: Omit<TableSelectionOverlayPluginProps, \"element\">;\r\n children: React.ReactNode;\r\n}\r\n\r\nexport function TableSelectionOverlay({\r\n ref,\r\n children,\r\n pluginProps,\r\n}: TableSelectionOverlayProps) {\r\n const rootElementRef = useRef(document.createElement(\"div\"));\r\n\r\n useEffect(() => {\r\n if (typeof ref === \"function\") {\r\n ref(rootElementRef.current);\r\n } else if (ref) {\r\n ref.current = rootElementRef.current;\r\n }\r\n }, [ref]);\r\n\r\n useEffect(() => {\r\n const editor = pluginProps.editor;\r\n const rootElement = rootElementRef.current;\r\n\r\n rootElement.style.visibility = \"hidden\";\r\n rootElement.style.position = \"absolute\";\r\n rootElement.className = cn(\"border-2 border-primary pointer-events-none\");\r\n\r\n if (editor.isDestroyed) {\r\n return;\r\n }\r\n\r\n const plugin = TableSelectionOverlayPlugin({\r\n ...pluginProps,\r\n element: rootElement,\r\n });\r\n\r\n editor.registerPlugin(plugin);\r\n\r\n return () => {\r\n editor.unregisterPlugin(pluginProps.pluginKey);\r\n window.requestAnimationFrame(() => {\r\n if (rootElement.parentNode) {\r\n rootElement.parentNode.removeChild(rootElement);\r\n }\r\n });\r\n };\r\n }, [pluginProps]);\r\n\r\n return createPortal(<>{children}</>, rootElementRef.current);\r\n}\r\n","import { PluginKey } from \"@tiptap/pm/state\";\r\n\r\nexport interface TableControlState {\r\n visible: boolean;\r\n}\r\n\r\nexport const addRowPluginKey =\r\n new PluginKey<TableControlState>(\"add-row-plugin\");\r\n\r\nexport const addColumnPluginKey =\r\n new PluginKey<TableControlState>(\"add-column-plugin\");","import React, { useRef } from \"react\";\r\nimport { Plus } from \"lucide-react\";\r\nimport { Editor } from \"@tiptap/react\";\r\nimport { deleteEmptyRowFromBottom, insertRowAtBottom } from \"./utils/table\";\r\n\r\nexport function AddRowButton({ editor }: { editor: Editor }) {\r\n const dragMetrics = useRef({\r\n isDragging: false,\r\n startY: 0,\r\n rowHeight: 0,\r\n tablePos: 0,\r\n rowsTracked: 0,\r\n });\r\n const handleMouseDown = (e: React.MouseEvent<HTMLButtonElement>) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n\r\n const container = e.currentTarget.parentElement;\r\n const tablePosAttr = container?.getAttribute(\"data-table-pos\");\r\n const tableElement = container\r\n ?.closest(\".table-wrapper\")\r\n ?.querySelector(\"table\");\r\n const lastRowElement = tableElement?.querySelector(\"tr:last-child\");\r\n\r\n if (!tablePosAttr || !lastRowElement) return;\r\n\r\n dragMetrics.current = {\r\n isDragging: true,\r\n startY: e.clientY,\r\n rowHeight: lastRowElement.getBoundingClientRect().height,\r\n tablePos: parseInt(tablePosAttr, 10),\r\n rowsTracked: 0,\r\n };\r\n\r\n window.addEventListener(\"mousemove\", handleMouseMove);\r\n window.addEventListener(\"mouseup\", handleMouseUp);\r\n };\r\n\r\n const handleMouseMove = (e: MouseEvent) => {\r\n const metrics = dragMetrics.current;\r\n if (!metrics.isDragging) return;\r\n\r\n const deltaY = e.clientY - metrics.startY;\r\n const targetRowChange = Math.round(deltaY / metrics.rowHeight);\r\n\r\n if (targetRowChange === metrics.rowsTracked) return;\r\n\r\n // Dragging Down -> Add Row\r\n if (targetRowChange > metrics.rowsTracked) {\r\n const success = insertRowAtBottom(editor, metrics.tablePos, false);\r\n if (success) metrics.rowsTracked += 1;\r\n }\r\n // Dragging Up -> Delete Empty Row\r\n else if (targetRowChange < metrics.rowsTracked) {\r\n const success = deleteEmptyRowFromBottom(editor, metrics.tablePos, false);\r\n if (success) metrics.rowsTracked -= 1;\r\n }\r\n };\r\n\r\n const handleMouseUp = (e: MouseEvent) => {\r\n const metrics = dragMetrics.current;\r\n const totalDeltaY = Math.abs(e.clientY - metrics.startY);\r\n\r\n if (totalDeltaY < 4) {\r\n insertRowAtBottom(editor, metrics.tablePos, true);\r\n } else {\r\n // Commit the final drag outcome macro safely to history\r\n editor.view.dispatch(editor.state.tr.setMeta(\"addToHistory\", true));\r\n }\r\n\r\n metrics.isDragging = false;\r\n window.removeEventListener(\"mousemove\", handleMouseMove);\r\n window.removeEventListener(\"mouseup\", handleMouseUp);\r\n };\r\n return (\r\n <button\r\n className=\"w-full h-3 bg-accent flex justify-center items-center rounded-full mt-1 cursor-ns-resize selection:bg-transparent\"\r\n onMouseDown={handleMouseDown}\r\n >\r\n <Plus size={12} />\r\n </button>\r\n );\r\n}\r\n","import { Plugin, PluginKey } from \"@tiptap/pm/state\";\r\nimport { Editor } from \"@tiptap/react\";\r\nimport { TableControlState } from \"./plugin-keys\";\r\n\r\nexport interface TableAddRowPluginProps {\r\n editor: Editor;\r\n element: HTMLElement;\r\n pluginKey: PluginKey<TableControlState>;\r\n}\r\n\r\nexport function TableAddRowPlugin({\r\n editor,\r\n element,\r\n pluginKey,\r\n}: TableAddRowPluginProps) {\r\n\r\n // Quick helper to safely set opacity transitions\r\n const setVisibility = (opacity: \"0\" | \"1\") => {\r\n element.style.transition = \"opacity 0.2s ease\"; // Smooth fading\r\n element.style.opacity = opacity;\r\n };\r\n\r\n\r\n return new Plugin<TableControlState>({\r\n key: pluginKey,\r\n props: {\r\n handleDOMEvents: {\r\n mouseover(view, event) {\r\n const target = event.target as HTMLElement;\r\n\r\n const insideControls = target.closest(\".table-controls\");\r\n const lastRow = target.closest(\"table tr:last-child\");\r\n\r\n\r\n if (insideControls || lastRow) {\r\n // Find the master wrapper to locate/move our button if necessary\r\n const tableWrapper = target.closest(\".table-wrapper\");\r\n const tableElement = tableWrapper?.querySelector(\"table\");\r\n const controls = tableWrapper?.querySelector(\".table-controls\") as HTMLElement | null;\r\n\r\n if (controls && tableElement) {\r\n const pos = view.posAtDOM(tableElement, 0);\r\n\r\n element.setAttribute(\"data-table-pos\", String(pos - 1));\r\n\r\n if (!controls.contains(element)) {\r\n element.style.position = \"\";\r\n element.style.width = `${tableElement.offsetWidth}px`\r\n controls.appendChild(element);\r\n }\r\n setVisibility(\"1\");\r\n return false;\r\n }\r\n }\r\n setVisibility(\"0\");\r\n return false;\r\n },\r\n\r\n mouseleave(view, event) {\r\n // Fade it out completely when mouse leaves the editor view canvas\r\n setVisibility(\"0\");\r\n return false;\r\n }\r\n }\r\n },\r\n view() {\r\n // Initial state is hidden until a hover happens\r\n element.style.opacity = \"0\";\r\n element.style.visibility = 'visible'\r\n\r\n return {\r\n destroy() {\r\n element.remove();\r\n },\r\n };\r\n },\r\n });\r\n}","import { useEffect, useRef } from \"react\";\r\nimport { createPortal } from \"react-dom\";\r\nimport {\r\n TableAddRowPlugin,\r\n TableAddRowPluginProps,\r\n} from \"./plugins/add-row-plugin\";\r\n\r\nexport interface TableAddRowColumnHandleProps {\r\n ref?: React.ForwardedRef<HTMLDivElement>;\r\n children: React.ReactNode;\r\n pluginProps: Omit<TableAddRowPluginProps, \"element\">;\r\n}\r\n\r\nexport const TableAddRowColumnHandle = ({\r\n ref,\r\n children,\r\n pluginProps,\r\n}: TableAddRowColumnHandleProps) => {\r\n const rootElementRef = useRef(document.createElement(\"div\"));\r\n\r\n useEffect(() => {\r\n if (typeof ref === \"function\") {\r\n ref(rootElementRef.current);\r\n } else if (ref) {\r\n ref.current = rootElementRef.current;\r\n }\r\n }, [ref]);\r\n\r\n useEffect(() => {\r\n const editor = pluginProps.editor;\r\n const rootElement = rootElementRef.current;\r\n\r\n rootElement.style.visibility = \"hidden\";\r\n rootElement.style.position = \"absolute\";\r\n\r\n if (editor.isDestroyed) {\r\n return;\r\n }\r\n\r\n const plugin = TableAddRowPlugin({\r\n ...pluginProps,\r\n element: rootElement,\r\n });\r\n\r\n editor.registerPlugin(plugin);\r\n\r\n return () => {\r\n editor.unregisterPlugin(pluginProps.pluginKey);\r\n window.requestAnimationFrame(() => {\r\n if (rootElement.parentNode) {\r\n rootElement.parentNode.removeChild(rootElement);\r\n }\r\n });\r\n };\r\n }, [pluginProps]);\r\n\r\n return createPortal(<>{children}</>, rootElementRef.current);\r\n};\r\n","import { useMemo } from \"react\";\r\nimport { TableMenuHandle, TableMenuHandleProps } from \"./TableMenuHandle\";\r\nimport { Editor } from \"@tiptap/core\";\r\nimport {\r\n columnMenuPluginKey,\r\n rowMenuPluginKey,\r\n} from \"./plugins/table-menu-handle-plugin\";\r\nimport { CellMenuPopover } from \"./popover/CellMenuPopover\";\r\nimport {\r\n TableSelectionOverlay,\r\n TableSelectionOverlayProps,\r\n} from \"./TableSelectionOverlay\";\r\nimport { PluginKey } from \"@tiptap/pm/state\";\r\nimport { addRowPluginKey } from \"./plugins/plugin-keys\";\r\nimport { AddRowButton } from \"./AddRowButton\";\r\nimport { TableAddRowColumnHandle } from \"./TableAddRowColumnHandle\";\r\nimport { TableMenuPopover } from \"./popover/TableMenuPopover\";\r\n\r\nexport const TableHandle = ({ editor }: { editor: Editor | null }) => {\r\n const columnMenuPluginProps = useMemo(() => {\r\n if (!editor) {\r\n return undefined;\r\n }\r\n return {\r\n editor,\r\n menuType: \"column\",\r\n pluginKey: columnMenuPluginKey,\r\n options: {\r\n placement: \"top-start\",\r\n offset: {\r\n mainAxis: 4,\r\n },\r\n },\r\n } satisfies TableMenuHandleProps[\"pluginProps\"];\r\n }, [editor]);\r\n\r\n const rowMenuPluginProps = useMemo(() => {\r\n if (!editor) {\r\n return undefined;\r\n }\r\n return {\r\n editor,\r\n menuType: \"row\",\r\n pluginKey: rowMenuPluginKey,\r\n options: {\r\n placement: \"left-start\",\r\n offset: {\r\n mainAxis: 4,\r\n },\r\n },\r\n } satisfies TableMenuHandleProps[\"pluginProps\"];\r\n }, [editor]);\r\n\r\n // Định danh phải ổn định. TableAddRowColumnHandle đưa object này vào deps\r\n // của useEffect, và mỗi lần deps đổi nó gọi `editor.unregisterPlugin(...)`.\r\n // ProseMirror phản ứng bằng cách huỷ TOÀN BỘ plugin view rồi dựng lại — kể\r\n // cả plugin suggestion, nên `onExit` chạy và menu slash bị gỡ khỏi DOM.\r\n // Hệ quả: app chủ nào đẩy `onChange` vào state là menu tắt ngay phím đầu.\r\n const addRowPluginProps = useMemo(() => {\r\n if (!editor) {\r\n return undefined;\r\n }\r\n return { editor, pluginKey: addRowPluginKey };\r\n }, [editor]);\r\n\r\n const tableSelectionOverlayProps = useMemo(() => {\r\n if (!editor) {\r\n return undefined;\r\n }\r\n return {\r\n editor,\r\n pluginKey: new PluginKey(\"table-selection-overlay\"),\r\n } satisfies TableSelectionOverlayProps[\"pluginProps\"];\r\n }, [editor]);\r\n\r\n if (!editor) {\r\n return null;\r\n }\r\n return (\r\n <>\r\n <div>\r\n {columnMenuPluginProps && (\r\n <TableMenuHandle pluginProps={columnMenuPluginProps}>\r\n {/* <ColumnMenuPopover editor={editor} /> */}\r\n\r\n <TableMenuPopover\r\n pluginKey={columnMenuPluginKey}\r\n type=\"column\"\r\n editor={editor}\r\n />\r\n </TableMenuHandle>\r\n )}\r\n {rowMenuPluginProps && (\r\n <TableMenuHandle pluginProps={rowMenuPluginProps}>\r\n {/* <RowMenuPopover editor={editor} /> */}\r\n\r\n <TableMenuPopover\r\n pluginKey={rowMenuPluginKey}\r\n type=\"row\"\r\n editor={editor}\r\n />\r\n </TableMenuHandle>\r\n )}\r\n </div>\r\n\r\n {addRowPluginProps && (\r\n <TableAddRowColumnHandle pluginProps={addRowPluginProps}>\r\n <AddRowButton editor={editor} />\r\n </TableAddRowColumnHandle>\r\n )}\r\n\r\n {/* <TableAddColumnHandle\r\n pluginProps={{\r\n editor,\r\n pluginKey: addColumnPluginKey,\r\n }}\r\n >\r\n <AddColumnButton editor={editor} />\r\n </TableAddColumnHandle> */}\r\n\r\n {tableSelectionOverlayProps && (\r\n <TableSelectionOverlay pluginProps={tableSelectionOverlayProps}>\r\n <CellMenuPopover editor={editor} />\r\n </TableSelectionOverlay>\r\n )}\r\n </>\r\n );\r\n};\r\n","import React from \"react\";\r\nimport { TableOfContentDataItem } from \"@tiptap/extension-table-of-contents\";\r\nimport { cn } from \"~/lib/utils\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\ninterface TocUIListProps {\r\n items: TableOfContentDataItem[];\r\n scrollOver: boolean;\r\n}\r\n\r\nexport const TocUIList: React.FC<TocUIListProps> = ({ items, scrollOver }) => {\r\n const t = useMessages();\r\n const handleScroll = (id: string) => {\r\n const element = document.getElementById(id);\r\n if (element) {\r\n element.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\r\n }\r\n };\r\n\r\n if (items.length === 0) {\r\n return (\r\n <p className=\"text-xs text-muted-foreground italic p-2\">\r\n {t.toc.empty}\r\n </p>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"toc-sidebar-progress\">\r\n {items.map((item) => (\r\n <div\r\n onClick={() => handleScroll(item.id)}\r\n key={item.id}\r\n className={cn(`toc-sidebar-progress-line`, {\r\n \"toc-sidebar-progress-line-active\": scrollOver\r\n ? item.isScrolledOver\r\n : item.isActive,\r\n })}\r\n style={{ \"--toc-depth\": item.level } as React.CSSProperties}\r\n ></div>\r\n ))}\r\n </div>\r\n );\r\n};\r\n\r\n{\r\n /* <button\r\n onClick={() => handleScroll(item.id)}\r\n className=\"text-left text-sm text-muted-foreground hover:text-primary transition-colors cursor-pointer block truncate w-full\"\r\n>\r\n {item.textContent}\r\n</button>; */\r\n}\r\n","import { TableOfContentDataItem } from \"@tiptap/extension-table-of-contents\";\r\nimport { cn } from \"~/lib/utils\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\nconst TocSidebarContent = ({\r\n items,\r\n scrollOver,\r\n}: {\r\n items: TableOfContentDataItem[];\r\n scrollOver: boolean;\r\n}) => {\r\n const t = useMessages();\r\n return (\r\n <div className=\"flex flex-col gap-1 w-full text-sm text-neutral-600 dark:text-neutral-400\">\r\n {/* Sidebar Header */}\r\n <div className=\"px-3 py-2 text-xs font-semibold tracking-wider text-neutral-400 uppercase select-none\">\r\n {t.toc.title}\r\n </div>\r\n\r\n {items.map((item) => (\r\n <button\r\n key={item.id || item.textContent}\r\n onClick={() => {\r\n document\r\n .getElementById(item.id)\r\n ?.scrollIntoView({ behavior: \"smooth\" });\r\n }}\r\n className={cn(\r\n ` flex items-center w-full px-3 py-1.5 text-left rounded-md transition-all duration-200 ease-in-out group relative hover:bg-accent`,\r\n {\r\n \"bg-accent text-muted-foreground!\": !scrollOver && item.isActive,\r\n \"text-gray-500\": scrollOver && item.isScrolledOver\r\n },\r\n )}\r\n style={{\r\n paddingLeft: `${((item.level || 1) - 1) * 12 + 12}px`,\r\n }}\r\n >\r\n {/* Active Left Indicator Bar */}\r\n {/* {isActive && (\r\n <span className=\"transition-all duration-200 absolute left-0 top-1/2 -translate-y-1/2 w-1 h-4 bg-blue-500 rounded-r\" />\r\n )} */}\r\n\r\n <span className=\"truncate\">{item.textContent}</span>\r\n </button>\r\n ))}\r\n </div>\r\n );\r\n};\r\n\r\nexport default TocSidebarContent;\r\n","import React from \"react\";\r\nimport { ScrollArea } from \"../ui/scroll-area\";\r\nimport { TableOfContentDataItem } from \"@tiptap/extension-table-of-contents\";\r\nimport { cn } from \"~/lib/utils\";\r\n\r\nconst TocPopover = ({ items }: { items: TableOfContentDataItem[] }) => {\r\n return (\r\n <div className=\"toc-sidebar-nav\">\r\n <div className=\"toc-sidebar-popover w-62.5 p-2 pr-px rounded-2xl border border-border shadow-xl bg-popover\">\r\n <ScrollArea className=\"h-96 pr-4 **:space-y-px\">\r\n {items.map((item) => (\r\n <div\r\n key={item.id}\r\n onClick={() => {\r\n document\r\n .getElementById(item.id)\r\n ?.scrollIntoView({ behavior: \"smooth\" });\r\n }}\r\n className={cn(\r\n `toc-sidebar-item text-sm hover:bg-secondary hover:text-foreground text-muted-foreground rounded-md `,\r\n {\r\n \"bg-secondary text-foreground\": item.isActive,\r\n },\r\n )}\r\n style={{ \"--toc-depth\": item.level } as React.CSSProperties}\r\n >\r\n {item.textContent}\r\n </div>\r\n ))}\r\n </ScrollArea>\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default TocPopover;\r\n","import React, { useState, useRef } from \"react\";\r\nimport { Editor, useEditorState } from \"@tiptap/react\";\r\nimport { TocUIList } from \"./TocUIList\";\r\nimport { TableOfContentDataItem } from \"@tiptap/extension-table-of-contents\";\r\nimport { cn } from \"~/lib/utils\";\r\nimport TocSidebarContent from \"./TocSidebarContent\";\r\nimport TocPopover from \"./TocPopover\";\r\n\r\nconst Memorized = React.memo(TocUIList);\r\n\r\ninterface PropsType {\r\n editor: Editor | null;\r\n variant?: \"content\" | \"line\";\r\n scrollOver?: boolean;\r\n}\r\n\r\nexport const TocSidebar = ({\r\n editor,\r\n variant = \"content\",\r\n scrollOver = false,\r\n}: PropsType) => {\r\n if (!editor) return null;\r\n const [activeId, setActiveId] = useState<string | null>(null);\r\n\r\n const items = useEditorState({\r\n editor,\r\n selector: (ctx) =>\r\n (ctx.editor.storage.tableOfContents?.content ||\r\n []) as TableOfContentDataItem[],\r\n });\r\n\r\n const visibleHeadingsRef = useRef<Record<string, boolean>>({});\r\n\r\n // useEffect(() => {\r\n // if (!editor || items.length === 0) return;\r\n\r\n // visibleHeadingsRef.current = {};\r\n\r\n // const observerOptions = {\r\n // root: null,\r\n // rootMargin: \"-80px 0px -100px 0px\",\r\n // threshold: 0,\r\n // };\r\n\r\n // const handleIntersection = (entries: IntersectionObserverEntry[]) => {\r\n // entries.forEach((entry) => {\r\n // visibleHeadingsRef.current[entry.target.id] = entry.isIntersecting;\r\n // });\r\n\r\n // const activeItem = items.find(\r\n // (item) => visibleHeadingsRef.current[item.id],\r\n // );\r\n\r\n // if (activeItem) {\r\n // setActiveId(activeItem.id);\r\n // }\r\n // };\r\n\r\n // const observer = new IntersectionObserver(\r\n // handleIntersection,\r\n // observerOptions,\r\n // );\r\n\r\n // items.forEach((item) => {\r\n // const el = document.getElementById(item.id);\r\n // if (el) observer.observe(el);\r\n // });\r\n\r\n // return () => {\r\n // observer.disconnect();\r\n // };\r\n // }, [items, editor]);\r\n\r\n // const enhancedItems = items.map((item) => ({\r\n // ...item,\r\n // isActive: item.id === activeId,\r\n // isScrolledOver: item.id === activeId,\r\n // }));\r\n\r\n const handleScroll = (id: string) => {\r\n const element = document.getElementById(id);\r\n if (element) {\r\n element.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\r\n }\r\n };\r\n\r\n return (\r\n <aside\r\n className={cn(\"toc-sidebar not-prose \", {\r\n \"sticky top-20 self-start h-fit\": variant === \"content\",\r\n relative: variant === \"line\",\r\n })}\r\n >\r\n <div\r\n className={cn(\"toc-sidebar-wrapper\", {\r\n \"w-60 p-2 h-full border-l flex flex-col\": variant === \"content\",\r\n // \"fixed top-1/2 right-0 translate-[0%,-50%]\": showLine,\r\n \"fixed top-1/2 right-4 -translate-y-1/2 z-50\": variant === \"line\",\r\n })}\r\n >\r\n {variant === \"line\" && (\r\n <div className=\"toc-sidebar-inner\">\r\n <Memorized items={items} scrollOver={scrollOver} />\r\n <TocPopover items={items} />\r\n </div>\r\n )}\r\n\r\n {variant === \"content\" && <TocSidebarContent items={items} scrollOver={scrollOver} />}\r\n </div> \r\n </aside>\r\n );\r\n};\r\n","import { useCallback, useLayoutEffect, useRef } from 'react';\r\n\r\nexport const useEvent = <T extends (...args: any[]) => any>(handler: T): T => {\r\n const handlerRef = useRef<T | null>(null);\r\n\r\n useLayoutEffect(() => {\r\n handlerRef.current = handler;\r\n }, [handler]);\r\n\r\n return useCallback((...args: Parameters<T>): ReturnType<T> => {\r\n if (handlerRef.current === null) {\r\n throw new Error('Handler is not assigned');\r\n }\r\n return handlerRef.current(...args);\r\n }, []) as T;\r\n};\r\n","/**\r\n * Calculates the aspect ratio from image dimensions\r\n * @param width - The width of the image\r\n * @param height - The height of the image\r\n * @returns The aspect ratio as width/height\r\n */\r\nexport function getAspectRatio(width: number, height: number): number {\r\n return width / height;\r\n}\r\n\r\n/**\r\n * Calculates new height based on width and aspect ratio\r\n * @param width - The new width\r\n * @param aspectRatio - The aspect ratio (width/height)\r\n * @returns The corresponding height\r\n */\r\nexport function getNewHeight(width: number, aspectRatio: number): number {\r\n if (width <= 0 || aspectRatio <= 0) {\r\n return 0;\r\n }\r\n return width / aspectRatio;\r\n}\r\n\r\n/**\r\n * Calculates new width based on height and aspect ratio\r\n * @param height - The new height\r\n * @param aspectRatio - The aspect ratio (width/height)\r\n * @returns The corresponding width\r\n */\r\nexport function getNewWidth(height: number, aspectRatio: number): number {\r\n return height * aspectRatio;\r\n}\r\n","import React, { CSSProperties, useCallback, useRef, useState } from \"react\";\r\nimport { NodeViewProps, NodeViewWrapper } from \"@tiptap/react\";\r\nimport { useEvent } from \"~/lib/use-event\";\r\nimport { getAspectRatio, getNewHeight } from \"~/lib/aspect-ratio\";\r\n\r\nconst MIN_WIDTH = 20;\r\nexport const IMAGE_MAX_WIDTH = 600;\r\nexport const IMAGE_MAX_HEIGHT = 400;\r\n\r\nexport default function ImageNodeView({\r\n node,\r\n editor,\r\n updateAttributes,\r\n getPos,\r\n}: NodeViewProps) {\r\n const { src, alignment, width: nodeWidth, height: nodeHeight } = node.attrs;\r\n const [isHovered, setIsHovered] = useState(false);\r\n\r\n const wrapperRef = useRef<HTMLDivElement>(null);\r\n // Change ref type back to HTMLDivElement since it targets the container div\r\n const containerRef = useRef<HTMLDivElement>(null);\r\n const imgRef = useRef<HTMLImageElement>(null);\r\n\r\n const handleMouseDown = useEvent(\r\n (event: React.MouseEvent<HTMLDivElement>) => {\r\n if (!imgRef.current || !containerRef.current) return;\r\n\r\n event.preventDefault();\r\n event.stopPropagation();\r\n\r\n const maxAllowedWidth = wrapperRef.current?.parentElement\r\n ? wrapperRef.current.parentElement.offsetWidth\r\n : IMAGE_MAX_WIDTH;\r\n\r\n const direction = event.currentTarget.dataset.direction || \"--\";\r\n const initialXPosition = event.clientX;\r\n const initialYPosition = event.clientY;\r\n\r\n const currentWidth = imgRef.current.offsetWidth;\r\n const currentHeight = imgRef.current.offsetHeight;\r\n\r\n let newWidth = currentWidth;\r\n let newHeight = currentHeight;\r\n\r\n const transformX = direction.charAt(1) === \"w\" ? -1 : 1;\r\n const transformY = direction.charAt(0) === \"n\" ? -1 : 1;\r\n\r\n const removeListeners = () => {\r\n window.removeEventListener(\"mousemove\", mouseMoveHandler, {\r\n capture: true,\r\n });\r\n window.removeEventListener(\"mouseup\", removeListeners, {\r\n capture: true,\r\n });\r\n\r\n const aspectRatio = getAspectRatio(newWidth, newHeight);\r\n\r\n // Finalize state change inside Tiptap schema model\r\n updateAttributes({ width: newWidth, height: newHeight, aspectRatio });\r\n };\r\n\r\n const mouseMoveHandler = (event: MouseEvent) => {\r\n newWidth = Math.max(\r\n currentWidth + transformX * (event.clientX - initialXPosition),\r\n MIN_WIDTH,\r\n );\r\n newHeight = Math.max(\r\n currentHeight + transformY * (event.clientY - initialYPosition),\r\n MIN_WIDTH,\r\n );\r\n\r\n if (newWidth > maxAllowedWidth) newWidth = maxAllowedWidth;\r\n if (newHeight > IMAGE_MAX_HEIGHT) newHeight = IMAGE_MAX_HEIGHT;\r\n\r\n if (node.attrs.lockAspectRatio) {\r\n const aspectRatio =\r\n node.attrs.aspectRatio &&\r\n isFinite(node.attrs.aspectRatio) &&\r\n node.attrs.aspectRatio > 0\r\n ? node.attrs.aspectRatio\r\n : currentWidth / currentHeight;\r\n newHeight = getNewHeight(newWidth, aspectRatio);\r\n }\r\n\r\n if (containerRef.current) {\r\n containerRef.current.style.width = `${newWidth}px`;\r\n // Update attribute dynamically for CSS selection targeting\r\n wrapperRef.current?.setAttribute(\"data-width\", String(newWidth));\r\n }\r\n\r\n if (!event.buttons) {\r\n return removeListeners();\r\n }\r\n };\r\n\r\n window.addEventListener(\"mousemove\", mouseMoveHandler, { capture: true });\r\n window.addEventListener(\"mouseup\", removeListeners, { capture: true });\r\n },\r\n );\r\n\r\n // 1. Calculate the final computed width value\r\n const finalWidth = nodeWidth ? `${nodeWidth}px` : \"auto\";\r\n\r\n return (\r\n <NodeViewWrapper\r\n onMouseEnter={() => setIsHovered(true)}\r\n onMouseLeave={() => setIsHovered(false)}\r\n className=\"tiptap-image transition-all duration-200 select-none\"\r\n data-align={alignment}\r\n data-width={nodeWidth || nodeWidth} // Handled via data attributes\r\n ref={wrapperRef}\r\n >\r\n <div\r\n className=\"tiptap-image-container\"\r\n style={{ width: finalWidth }}\r\n ref={containerRef}\r\n >\r\n <div className=\"tiptap-image-content relative\" >\r\n <img\r\n data-drag-handle\r\n ref={imgRef}\r\n contentEditable={false}\r\n draggable={false}\r\n className=\"tiptap-image-img cursor-pointer\"\r\n src={src}\r\n alt=\"example\"\r\n onDragStart={(e) => e.preventDefault()}\r\n style={\r\n {\r\n height: \"auto\",\r\n display: \"block\",\r\n userSelect: \"none\",\r\n WebkitUserDrag: \"none\",\r\n } as CSSProperties\r\n } // Make image follow container width\r\n />\r\n {isHovered && (\r\n <>\r\n <div\r\n onMouseDown={handleMouseDown}\r\n data-direction=\"-w\"\r\n className=\"tiptap-image-handle tiptap-image-handle-left\"\r\n ></div>\r\n <div\r\n onMouseDown={handleMouseDown}\r\n data-direction=\"-e\"\r\n className=\"tiptap-image-handle tiptap-image-handle-right\"\r\n ></div>\r\n </>\r\n )}\r\n </div>\r\n </div>\r\n </NodeViewWrapper>\r\n );\r\n}\r\n\r\n// const handleReplaceClick = () => {\r\n// const currentPosition = typeof getPos === \"function\" ? getPos() : null;\r\n// if (currentPosition === null || currentPosition == undefined) {\r\n// return;\r\n// }\r\n\r\n// editor\r\n// .chain()\r\n// .focus()\r\n// .deleteRange({\r\n// from: currentPosition,\r\n// to: currentPosition + node.nodeSize,\r\n// })\r\n// .insertContentAt(currentPosition, {\r\n// type: \"imageUpload\",\r\n// })\r\n// .run();\r\n// };\r\n","import Image from '@tiptap/extension-image'\r\nimport { mergeAttributes, NodePos, NodeViewProps, ReactNodeViewRenderer, ResizableNodeView } from '@tiptap/react'\r\nimport ImageNodeView from './ImageNodeView'\r\nimport { HTMLAttributes } from 'react'\r\n\r\nexport const CustomImage = Image.extend({\r\n draggable: true,\r\n // group: \"block\",\r\n // content: \"inline+\",\r\n // atom: true,\r\n addAttributes() {\r\n return {\r\n ...this.parent?.(),\r\n alignment: {\r\n default: 'center',\r\n\r\n },\r\n 'data-width': {\r\n default: 0\r\n },\r\n\r\n width: {\r\n default: 0,\r\n parseHTML: element => {\r\n const val = element.getAttribute('data-width') || element.style.width;\r\n return val ? parseInt(val, 10) : 0\r\n },\r\n renderHTML: attributes => {\r\n return attributes['data-width']\r\n }\r\n },\r\n \r\n // height: {\r\n // default: 0,\r\n // parseHTML: element => element.getAttribute('height') || parseInt(element.style.height, 10) || null,\r\n // renderHTML: attributes => {\r\n // if (!attributes) return {};\r\n // return { height: attributes.height }\r\n // }\r\n // },\r\n // aspectRatio: {\r\n // default: null,\r\n // parseHTML: element => element.getAttribute('data-aspect-ratio') || null,\r\n // renderHTML: attributes => {\r\n // if (!attributes.aspectRatio) return {};\r\n // return { 'data-aspect-ratio': attributes.aspectRatio }\r\n // }\r\n // }\r\n }\r\n },\r\n\r\n addNodeView() {\r\n // return ({node, getPos, HTMLAttributes}) => {\r\n // return new ResizableNodeView(ImageNodeView)\r\n // }\r\n\r\n return ReactNodeViewRenderer(ImageNodeView, {\r\n className: \"not-prose min-h-10\",\r\n attrs: { 'data-placeholder': \"Start writing...\", },\r\n })\r\n },\r\n\r\n\r\n\r\n})\r\n","import { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { AttachmentAttributes } from \"./AttachmentNode\";\n\nexport type AttachmentHandlers = {\n\t/**\n\t * Mở một tệp không có URL công khai.\n\t *\n\t * Node view chạy ngoài cây props của NotionEditor nên handler đi qua context.\n\t * Package không biết storage nào cả — nó đưa lại đúng thuộc tính đã lưu và\n\t * để app chủ quyết định lấy nội dung ra sao.\n\t */\n\tonOpenAttachment?: (attachment: AttachmentAttributes) => void;\n};\n\nconst AttachmentHandlersContext = createContext<AttachmentHandlers>({});\n\nexport const AttachmentHandlersProvider = ({\n\tonOpenAttachment,\n\tchildren,\n}: AttachmentHandlers & { children: ReactNode }) => {\n\tconst value = useMemo(() => ({ onOpenAttachment }), [onOpenAttachment]);\n\treturn (\n\t\t<AttachmentHandlersContext.Provider value={value}>\n\t\t\t{children}\n\t\t</AttachmentHandlersContext.Provider>\n\t);\n};\n\nexport const useAttachmentHandlers = () => useContext(AttachmentHandlersContext);\n","import type { NodeViewProps } from \"@tiptap/react\";\nimport { NodeViewWrapper } from \"@tiptap/react\";\nimport {\n\tDownload,\n\tFile as FileIcon,\n\tFileArchive,\n\tFileSpreadsheet,\n\tFileText,\n\tPresentation,\n} from \"lucide-react\";\nimport { useMessages } from \"~/i18n\";\nimport { useAttachmentHandlers } from \"./context\";\nimport type { AttachmentAttributes } from \"./AttachmentNode\";\n\n/**\n * Biểu tượng theo loại file.\n *\n * Chọn theo MIME trước, vì đó là thứ server khẳng định; chỉ khi thiếu MIME mới\n * đoán theo phần mở rộng của tên file.\n */\nconst iconFor = (mime: string | null, name: string | null) => {\n\tconst probe = `${mime ?? \"\"} ${name ?? \"\"}`.toLowerCase();\n\tif (/pdf/.test(probe)) return FileText;\n\tif (/word|document|\\.docx?$|\\.rtf|\\.odt/.test(probe)) return FileText;\n\tif (/sheet|excel|csv|\\.xlsx?$|\\.ods/.test(probe)) return FileSpreadsheet;\n\tif (/presentation|powerpoint|\\.pptx?$|\\.odp/.test(probe)) return Presentation;\n\tif (/zip|rar|7z|tar|gzip/.test(probe)) return FileArchive;\n\treturn FileIcon;\n};\n\n/** Dung lượng dạng người đọc được. */\nconst DOWNLOAD_CLASS =\n\t\"shrink-0 rounded-md p-2 text-muted-foreground hover:bg-[var(--tt-menu-item-hover-bg)] hover:text-foreground\";\n\nconst formatSize = (bytes: number) => {\n\tconst units = [\"B\", \"KB\", \"MB\", \"GB\"];\n\tlet value = bytes;\n\tlet unit = 0;\n\twhile (value >= 1024 && unit < units.length - 1) {\n\t\tvalue /= 1024;\n\t\tunit += 1;\n\t}\n\treturn `${value < 10 && unit > 0 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;\n};\n\nexport function AttachmentNodeView({ node, selected }: NodeViewProps) {\n\tconst t = useMessages();\n\tconst { onOpenAttachment } = useAttachmentHandlers();\n\tconst attrs = node.attrs as AttachmentAttributes;\n\tconst { src, name, size, mime } = attrs;\n\tconst Icon = iconFor(mime, name);\n\tconst label = name ?? src ?? \"\";\n\n\treturn (\n\t\t<NodeViewWrapper\n\t\t\tclassName=\"not-prose my-3\"\n\t\t\tdata-type=\"attachment\"\n\t\t\tdata-drag-handle\n\t\t>\n\t\t\t{/* contentEditable={false}: đây là khối chỉ để đọc, không cho con trỏ\n\t\t\t chui vào gõ đè lên. */}\n\t\t\t<div\n\t\t\t\tcontentEditable={false}\n\t\t\t\tclassName={`flex items-center gap-3 rounded-lg border p-3 transition-colors ${\n\t\t\t\t\tselected\n\t\t\t\t\t\t? \"border-[var(--tt-menu-item-hover-bg)] bg-[var(--tt-menu-item-hover-bg)]\"\n\t\t\t\t\t\t: \"border-border hover:bg-[var(--tt-menu-item-hover-bg)]\"\n\t\t\t\t}`}\n\t\t\t>\n\t\t\t\t<Icon className=\"size-8 shrink-0 text-muted-foreground\" />\n\n\t\t\t\t<div className=\"min-w-0 flex-1\">\n\t\t\t\t\t<div className=\"truncate font-medium text-sm\">{label}</div>\n\t\t\t\t\t{typeof size === \"number\" && size > 0 ? (\n\t\t\t\t\t\t<div className=\"text-muted-foreground text-xs\">{formatSize(size)}</div>\n\t\t\t\t\t) : null}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Có URL thì dùng thẻ <a> thật: giữ được chuột phải, mở tab mới,\n\t\t\t\t và không cần app chủ làm gì. Không có URL thì tệp nằm ở storage\n\t\t\t\t riêng tư, chỉ app chủ mới đổi được id lấy nội dung. */}\n\t\t\t\t{src ? (\n\t\t\t\t\t<a\n\t\t\t\t\t\thref={src}\n\t\t\t\t\t\tdownload={name ?? undefined}\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noreferrer\"\n\t\t\t\t\t\ttitle={t.attachment.download}\n\t\t\t\t\t\taria-label={t.attachment.download}\n\t\t\t\t\t\tclassName={DOWNLOAD_CLASS}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Download className=\"size-4\" />\n\t\t\t\t\t</a>\n\t\t\t\t) : attrs.fileId && onOpenAttachment ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => onOpenAttachment(attrs)}\n\t\t\t\t\t\ttitle={t.attachment.download}\n\t\t\t\t\t\taria-label={t.attachment.download}\n\t\t\t\t\t\tclassName={DOWNLOAD_CLASS}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Download className=\"size-4\" />\n\t\t\t\t\t</button>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</NodeViewWrapper>\n\t);\n}\n\nexport default AttachmentNodeView;\n","import { mergeAttributes, Node } from \"@tiptap/core\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\nimport { AttachmentNodeView } from \"./AttachmentNodeView\";\n\nexport type AttachmentAttributes = {\n\t/**\n\t * Id do storage của app chủ cấp, dùng khi tệp không có URL cố định.\n\t *\n\t * Storage riêng tư thường chỉ phát URL ngắn hạn, nên nhúng URL vào nội dung\n\t * là bảo đảm nó hỏng sau vài phút. Lưu id rồi hỏi app chủ lúc người dùng bấm\n\t * tải — xem prop `onOpenAttachment`.\n\t */\n\tfileId: string | null;\n\t/** URL công khai, dùng khi app chủ lưu ở storage công khai. */\n\tsrc: string | null;\n\t/** Tên hiển thị. Thường là tên file gốc. */\n\tname: string | null;\n\t/** Kích thước tính bằng byte, để hiển thị. Không có thì bỏ dòng đó. */\n\tsize: number | null;\n\t/** MIME type, dùng để chọn biểu tượng. */\n\tmime: string | null;\n};\n\ndeclare module \"@tiptap/core\" {\n\tinterface Commands<ReturnType> {\n\t\tattachment: {\n\t\t\t/** Chèn một file đã có URL vào document. */\n\t\t\tsetAttachment: (attrs: Partial<AttachmentAttributes>) => ReturnType;\n\t\t};\n\t}\n}\n\n/**\n * Khối file đính kèm: Word, PDF, bảng tính, zip…\n *\n * Node chỉ giữ URL và vài thông tin để hiển thị. Việc đưa file lên R2 là của\n * app chủ — package nhận URL đã có, đúng như ảnh.\n *\n * Ảnh có node riêng (`image`) vì nó hiển thị nội dung trực tiếp; file thường\n * chỉ có thể hiện thành một thẻ để tải về, nên tách node cho rõ.\n */\nexport const AttachmentNode = Node.create({\n\tname: \"attachment\",\n\tgroup: \"block\",\n\tatom: true,\n\tdraggable: true,\n\tselectable: true,\n\n\taddAttributes() {\n\t\treturn {\n\t\t\tfileId: { default: null },\n\t\t\tsrc: { default: null },\n\t\t\tname: { default: null },\n\t\t\tsize: { default: null, parseHTML: (el) => {\n\t\t\t\tconst raw = el.getAttribute(\"data-size\");\n\t\t\t\treturn raw ? Number(raw) : null;\n\t\t\t} },\n\t\t\tmime: { default: null },\n\t\t};\n\t},\n\n\tparseHTML() {\n\t\treturn [{ tag: 'div[data-type=\"attachment\"]' }];\n\t},\n\n\trenderHTML({ HTMLAttributes }) {\n\t\tconst { size, ...rest } = HTMLAttributes;\n\t\treturn [\n\t\t\t\"div\",\n\t\t\tmergeAttributes(\n\t\t\t\t{ \"data-type\": \"attachment\" },\n\t\t\t\trest,\n\t\t\t\tsize == null ? {} : { \"data-size\": String(size) },\n\t\t\t),\n\t\t];\n\t},\n\n\taddNodeView() {\n\t\treturn ReactNodeViewRenderer(AttachmentNodeView);\n\t},\n\n\taddCommands() {\n\t\treturn {\n\t\t\tsetAttachment:\n\t\t\t\t(attrs) =>\n\t\t\t\t({ commands }) =>\n\t\t\t\t\tcommands.insertContent({ type: this.name, attrs }),\n\t\t};\n\t},\n});\n\nexport default AttachmentNode;\n","import { useRef, useState } from \"react\";\nimport type { NodeViewProps } from \"@tiptap/react\";\nimport { NodeViewWrapper } from \"@tiptap/react\";\nimport { Paperclip } from \"lucide-react\";\nimport { useMessages } from \"~/i18n\";\nimport type { AttachmentUploadNodeOptions } from \"./AttachmentUploadNode\";\n\nconst isValidPosition = (pos: number | undefined): pos is number =>\n\ttypeof pos === \"number\" && pos >= 0;\n\nexport function AttachmentUploadNodeView(props: NodeViewProps) {\n\tconst t = useMessages();\n\tconst inputRef = useRef<HTMLInputElement>(null);\n\tconst [uploading, setUploading] = useState(false);\n\tconst options = props.extension.options as AttachmentUploadNodeOptions;\n\tconst maxSize = options.maxSize ?? 25 * 1024 * 1024;\n\n\t// Không dùng useFileUpload như khối ảnh: hook đó cố định kiểu trả về là URL,\n\t// còn ở đây app chủ có thể trả về id của storage riêng tư. Đính kèm cũng\n\t// không vẽ thanh tiến trình nên phần còn lại của hook là thừa.\n\tconst handleFile = async (file: File) => {\n\t\tif (!options.upload) {\n\t\t\toptions.onError?.(\n\t\t\t\tnew Error(\"[notion-editor] chưa cấu hình `uploadAttachment`.\"),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (file.size > maxSize) {\n\t\t\toptions.onError?.(\n\t\t\t\tnew Error(\n\t\t\t\t\t`[notion-editor] tệp vượt quá ${Math.round(maxSize / 1024 / 1024)}MB.`,\n\t\t\t\t),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tsetUploading(true);\n\t\ttry {\n\t\t\tconst uploaded = await options.upload(file);\n\t\t\tconst attrs =\n\t\t\t\ttypeof uploaded === \"string\"\n\t\t\t\t\t? { src: uploaded, fileId: null }\n\t\t\t\t\t: { src: uploaded.src ?? null, fileId: uploaded.fileId ?? null };\n\n\t\t\tconst pos = props.getPos();\n\t\t\tif (!isValidPosition(pos)) return;\n\n\t\t\t// Thay chính ô chờ này bằng node attachment, nên không để lại một ô\n\t\t\t// trống mồ côi sau khi tải xong.\n\t\t\tprops.editor\n\t\t\t\t.chain()\n\t\t\t\t.focus()\n\t\t\t\t.deleteRange({ from: pos, to: pos + 1 })\n\t\t\t\t.insertContentAt(pos, {\n\t\t\t\t\ttype: \"attachment\",\n\t\t\t\t\tattrs: {\n\t\t\t\t\t\t...attrs,\n\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\tsize: file.size,\n\t\t\t\t\t\tmime: file.type || null,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\t.run();\n\t\t\toptions.onSuccess?.(attrs.src ?? attrs.fileId ?? \"\");\n\t\t} catch (error) {\n\t\t\toptions.onError?.(\n\t\t\t\terror instanceof Error ? error : new Error(String(error)),\n\t\t\t);\n\t\t} finally {\n\t\t\tsetUploading(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<NodeViewWrapper className=\"not-prose my-3\" data-type=\"attachment-upload\">\n\t\t\t<div contentEditable={false}>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdisabled={uploading}\n\t\t\t\t\tonClick={() => inputRef.current?.click()}\n\t\t\t\t\tclassName=\"flex w-full items-center gap-3 rounded-lg border border-border border-dashed p-4 text-left text-muted-foreground text-sm hover:bg-[var(--tt-menu-item-hover-bg)] disabled:opacity-60\"\n\t\t\t\t>\n\t\t\t\t\t<Paperclip className=\"size-5 shrink-0\" />\n\t\t\t\t\t<span>\n\t\t\t\t\t\t{uploading ? t.attachment.uploading : t.attachment.clickToUpload}\n\t\t\t\t\t</span>\n\t\t\t\t</button>\n\t\t\t\t<input\n\t\t\t\t\tref={inputRef}\n\t\t\t\t\ttype=\"file\"\n\t\t\t\t\taccept={options.accept}\n\t\t\t\t\tclassName=\"hidden\"\n\t\t\t\t\tonChange={(event) => {\n\t\t\t\t\t\tconst file = event.target.files?.[0];\n\t\t\t\t\t\tevent.target.value = \"\";\n\t\t\t\t\t\tif (file) void handleFile(file);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</NodeViewWrapper>\n\t);\n}\n\nexport default AttachmentUploadNodeView;\n","import { mergeAttributes, Node } from \"@tiptap/react\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\nimport { AttachmentUploadNodeView } from \"./AttachmentUploadNodeView\";\n/**\n * Kết quả tải lên một tệp đính kèm.\n *\n * Trả `src` khi storage cấp URL cố định, trả `fileId` khi storage riêng tư\n * chỉ phát URL ngắn hạn — nhúng URL đó vào nội dung là bảo đảm nó hỏng sau vài\n * phút. Trả thẳng một chuỗi được hiểu là `src`, cho tương thích ngược.\n */\nexport type UploadedAttachment = { src?: string; fileId?: string };\n\nexport type AttachmentUploadFunction = (\n\tfile: File,\n\tonProgress?: (event: { progress: number }) => void,\n\tsignal?: AbortSignal,\n) => Promise<string | UploadedAttachment>;\n\nexport interface AttachmentUploadNodeOptions {\n\t/** Loại file được nhận. Mặc định mọi loại. */\n\taccept?: string;\n\t/** Số file tối đa mỗi lần. */\n\tlimit?: number;\n\t/** Dung lượng tối đa mỗi file, tính bằng byte. */\n\tmaxSize?: number;\n\t/**\n\t * Hàm đưa file lên và trả về URL công khai. App chủ cung cấp — package không\n\t * tự gọi storage nào.\n\t */\n\tupload?: AttachmentUploadFunction;\n\tonError?: (error: Error) => void;\n\tonSuccess?: (url: string) => void;\n\tHTMLAttributes: Record<string, unknown>;\n}\n\n/**\n * Ô trống chờ chọn file. Upload xong thì tự thay chính nó bằng node\n * `attachment` mang URL trả về.\n */\nexport const AttachmentUploadNode = Node.create<AttachmentUploadNodeOptions>({\n\tname: \"attachmentUpload\",\n\tgroup: \"block\",\n\tdraggable: true,\n\tselectable: true,\n\tatom: true,\n\n\taddOptions() {\n\t\treturn {\n\t\t\taccept: undefined,\n\t\t\tlimit: 1,\n\t\t\tmaxSize: 25 * 1024 * 1024,\n\t\t\tupload: undefined,\n\t\t\tonError: undefined,\n\t\t\tonSuccess: undefined,\n\t\t\tHTMLAttributes: {},\n\t\t};\n\t},\n\n\taddAttributes() {\n\t\treturn {\n\t\t\taccept: { default: this.options.accept },\n\t\t\tlimit: { default: this.options.limit },\n\t\t\tmaxSize: { default: this.options.maxSize },\n\t\t};\n\t},\n\n\tparseHTML() {\n\t\treturn [{ tag: 'div[data-type=\"attachment-upload\"]' }];\n\t},\n\n\trenderHTML({ HTMLAttributes }) {\n\t\treturn [\n\t\t\t\"div\",\n\t\t\tmergeAttributes({ \"data-type\": \"attachment-upload\" }, HTMLAttributes),\n\t\t];\n\t},\n\n\taddNodeView() {\n\t\treturn ReactNodeViewRenderer(AttachmentUploadNodeView);\n\t},\n});\n\nexport default AttachmentUploadNode;\n","import type { Node as PMNode } from \"@tiptap/pm/model\"\r\nimport type { Transaction } from \"@tiptap/pm/state\"\r\nimport { clsx, type ClassValue } from \"clsx\"\r\nimport {\r\n AllSelection,\r\n NodeSelection,\r\n Selection,\r\n TextSelection,\r\n} from \"@tiptap/pm/state\"\r\nimport { cellAround, CellSelection } from \"@tiptap/pm/tables\"\r\nimport {\r\n findParentNodeClosestToPos,\r\n type Editor,\r\n type NodeWithPos,\r\n} from \"@tiptap/react\"\r\n\r\nexport const MAX_FILE_SIZE = 5 * 1024 * 1024 // 5MB\r\n\r\nexport const MAC_SYMBOLS: Record<string, string> = {\r\n mod: \"⌘\",\r\n command: \"⌘\",\r\n meta: \"⌘\",\r\n ctrl: \"⌃\",\r\n control: \"⌃\",\r\n alt: \"⌥\",\r\n option: \"⌥\",\r\n shift: \"⇧\",\r\n backspace: \"Del\",\r\n delete: \"⌦\",\r\n enter: \"⏎\",\r\n escape: \"⎋\",\r\n capslock: \"⇪\",\r\n} as const\r\n\r\nexport const SR_ONLY = {\r\n position: \"absolute\",\r\n width: \"1px\",\r\n height: \"1px\",\r\n padding: 0,\r\n margin: \"-1px\",\r\n overflow: \"hidden\",\r\n clip: \"rect(0, 0, 0, 0)\",\r\n whiteSpace: \"nowrap\",\r\n borderWidth: 0,\r\n} as const\r\n\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return clsx(inputs)\r\n}\r\n\r\n/**\r\n * Determines if the current platform is macOS\r\n * @returns boolean indicating if the current platform is Mac\r\n */\r\nexport function isMac(): boolean {\r\n return (\r\n typeof navigator !== \"undefined\" &&\r\n navigator.platform.toLowerCase().includes(\"mac\")\r\n )\r\n}\r\n\r\n/**\r\n * Formats a shortcut key based on the platform (Mac or non-Mac)\r\n * @param key - The key to format (e.g., \"ctrl\", \"alt\", \"shift\")\r\n * @param isMac - Boolean indicating if the platform is Mac\r\n * @param capitalize - Whether to capitalize the key (default: true)\r\n * @returns Formatted shortcut key symbol\r\n */\r\nexport const formatShortcutKey = (\r\n key: string,\r\n isMac: boolean,\r\n capitalize: boolean = true\r\n) => {\r\n if (isMac) {\r\n const lowerKey = key.toLowerCase()\r\n return MAC_SYMBOLS[lowerKey] || (capitalize ? key.toUpperCase() : key)\r\n }\r\n\r\n return capitalize ? key.charAt(0).toUpperCase() + key.slice(1) : key\r\n}\r\n\r\n/**\r\n * Parses a shortcut key string into an array of formatted key symbols\r\n * @param shortcutKeys - The string of shortcut keys (e.g., \"ctrl-alt-shift\")\r\n * @param delimiter - The delimiter used to split the keys (default: \"-\")\r\n * @param capitalize - Whether to capitalize the keys (default: true)\r\n * @returns Array of formatted shortcut key symbols\r\n */\r\nexport const parseShortcutKeys = (props: {\r\n shortcutKeys: string | undefined\r\n delimiter?: string\r\n capitalize?: boolean\r\n}) => {\r\n const { shortcutKeys, delimiter = \"+\", capitalize = true } = props\r\n\r\n if (!shortcutKeys) return []\r\n\r\n return shortcutKeys\r\n .split(delimiter)\r\n .map((key) => key.trim())\r\n .map((key) => formatShortcutKey(key, isMac(), capitalize))\r\n}\r\n\r\n/**\r\n * Checks if a mark exists in the editor schema\r\n * @param markName - The name of the mark to check\r\n * @param editor - The editor instance\r\n * @returns boolean indicating if the mark exists in the schema\r\n */\r\nexport const isMarkInSchema = (\r\n markName: string,\r\n editor: Editor | null\r\n): boolean => {\r\n if (!editor?.schema) return false\r\n return editor.schema.spec.marks.get(markName) !== undefined\r\n}\r\n\r\n/**\r\n * Checks if a node exists in the editor schema\r\n * @param nodeName - The name of the node to check\r\n * @param editor - The editor instance\r\n * @returns boolean indicating if the node exists in the schema\r\n */\r\nexport const isNodeInSchema = (\r\n nodeName: string,\r\n editor: Editor | null\r\n): boolean => {\r\n if (!editor?.schema) return false\r\n return editor.schema.spec.nodes.get(nodeName) !== undefined\r\n}\r\n\r\n/**\r\n * Moves the focus to the next node in the editor\r\n * @param editor - The editor instance\r\n * @returns boolean indicating if the focus was moved\r\n */\r\nexport function focusNextNode(editor: Editor) {\r\n\r\n const { state, view } = editor\r\n const { doc, selection } = state\r\n\r\n const nextSel = Selection.findFrom(selection.$to, 1, true)\r\n if (nextSel) {\r\n view.dispatch(state.tr.setSelection(nextSel).scrollIntoView())\r\n return true\r\n }\r\n\r\n const paragraphType = state.schema.nodes.paragraph\r\n if (!paragraphType) {\r\n console.warn(\"No paragraph node type found in schema.\")\r\n return false\r\n }\r\n\r\n const end = doc.content.size\r\n const para = paragraphType.create()\r\n let tr = state.tr.insert(end, para)\r\n\r\n // Place the selection inside the new paragraph\r\n const $inside = tr.doc.resolve(end + 1)\r\n tr = tr.setSelection(TextSelection.near($inside)).scrollIntoView()\r\n view.dispatch(tr)\r\n return true\r\n}\r\n\r\n/**\r\n * Checks if a value is a valid number (not null, undefined, or NaN)\r\n * @param value - The value to check\r\n * @returns boolean indicating if the value is a valid number\r\n */\r\nexport function isValidPosition(pos: number | null | undefined): pos is number {\r\n return typeof pos === \"number\" && pos >= 0\r\n}\r\n\r\n/**\r\n * Checks if one or more extensions are registered in the Tiptap editor.\r\n * @param editor - The Tiptap editor instance\r\n * @param extensionNames - A single extension name or an array of names to check\r\n * @returns True if at least one of the extensions is available, false otherwise\r\n */\r\nexport function isExtensionAvailable(\r\n editor: Editor | null,\r\n extensionNames: string | string[]\r\n): boolean {\r\n if (!editor) return false\r\n\r\n const names = Array.isArray(extensionNames)\r\n ? extensionNames\r\n : [extensionNames]\r\n\r\n const found = names.some((name) =>\r\n editor.extensionManager.extensions.some((ext) => ext.name === name)\r\n )\r\n\r\n if (!found) {\r\n console.warn(\r\n `None of the extensions [${names.join(\", \")}] were found in the editor schema. Ensure they are included in the editor configuration.`\r\n )\r\n }\r\n\r\n return found\r\n}\r\n\r\n/**\r\n * Finds a node at the specified position with error handling\r\n * @param editor The Tiptap editor instance\r\n * @param position The position in the document to find the node\r\n * @returns The node at the specified position, or null if not found\r\n */\r\nexport function findNodeAtPosition(editor: Editor, position: number) {\r\n try {\r\n const node = editor.state.doc.nodeAt(position)\r\n if (!node) {\r\n console.warn(`No node found at position ${position}`)\r\n return null\r\n }\r\n return node\r\n } catch (error) {\r\n console.error(`Error getting node at position ${position}:`, error)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Finds the position and instance of a node in the document\r\n * @param props Object containing editor, node (optional), and nodePos (optional)\r\n * @param props.editor The Tiptap editor instance\r\n * @param props.node The node to find (optional if nodePos is provided)\r\n * @param props.nodePos The position of the node to find (optional if node is provided)\r\n * @returns An object with the position and node, or null if not found\r\n */\r\nexport function findNodePosition(props: {\r\n editor: Editor | null\r\n node?: PMNode | null\r\n nodePos?: number | null\r\n}): { pos: number; node: PMNode } | null {\r\n const { editor, node, nodePos } = props\r\n\r\n if (!editor || !editor.state?.doc) return null\r\n\r\n // Zero is valid position\r\n const hasValidNode = node !== undefined && node !== null\r\n const hasValidPos = isValidPosition(nodePos)\r\n\r\n if (!hasValidNode && !hasValidPos) {\r\n return null\r\n }\r\n\r\n // First search for the node in the document if we have a node\r\n if (hasValidNode) {\r\n let foundPos = -1\r\n let foundNode: PMNode | null = null\r\n\r\n editor.state.doc.descendants((currentNode, pos) => {\r\n // TODO: Needed?\r\n // if (currentNode.type && currentNode.type.name === node!.type.name) {\r\n if (currentNode === node) {\r\n foundPos = pos\r\n foundNode = currentNode\r\n return false\r\n }\r\n return true\r\n })\r\n\r\n if (foundPos !== -1 && foundNode !== null) {\r\n return { pos: foundPos, node: foundNode }\r\n }\r\n }\r\n\r\n // If we have a valid position, use findNodeAtPosition\r\n if (hasValidPos) {\r\n const nodeAtPos = findNodeAtPosition(editor, nodePos!)\r\n if (nodeAtPos) {\r\n return { pos: nodePos!, node: nodeAtPos }\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\n/**\r\n * Determines whether the current selection contains a node whose type matches\r\n * any of the provided node type names.\r\n * @param editor Tiptap editor instance\r\n * @param nodeTypeNames List of node type names to match against\r\n * @param checkAncestorNodes Whether to check ancestor node types up the depth chain\r\n */\r\nexport function isNodeTypeSelected(\r\n editor: Editor | null,\r\n nodeTypeNames: string[] = [],\r\n checkAncestorNodes: boolean = false\r\n): boolean {\r\n if (!editor || !editor.state.selection) return false\r\n\r\n const { selection } = editor.state\r\n if (selection.empty) return false\r\n\r\n // Direct node selection check\r\n if (selection instanceof NodeSelection) {\r\n const selectedNode = selection.node\r\n return selectedNode ? nodeTypeNames.includes(selectedNode.type.name) : false\r\n }\r\n\r\n // Depth-based ancestor node check\r\n if (checkAncestorNodes) {\r\n const { $from } = selection\r\n for (let depth = $from.depth; depth > 0; depth--) {\r\n const ancestorNode = $from.node(depth)\r\n if (nodeTypeNames.includes(ancestorNode.type.name)) {\r\n return true\r\n }\r\n }\r\n }\r\n\r\n return false\r\n}\r\n\r\n/**\r\n * Check whether the current selection is fully within nodes\r\n * whose type names are in the provided `types` list.\r\n *\r\n * - NodeSelection → checks the selected node.\r\n * - Text/AllSelection → ensures all textblocks within [from, to) are allowed.\r\n */\r\nexport function selectionWithinConvertibleTypes(\r\n editor: Editor,\r\n types: string[] = []\r\n): boolean {\r\n if (!editor || types.length === 0) return false\r\n\r\n const { state } = editor\r\n const { selection } = state\r\n const allowed = new Set(types)\r\n\r\n if (selection instanceof NodeSelection) {\r\n const nodeType = selection.node?.type?.name\r\n return !!nodeType && allowed.has(nodeType)\r\n }\r\n\r\n if (selection instanceof TextSelection || selection instanceof AllSelection) {\r\n let valid = true\r\n state.doc.nodesBetween(selection.from, selection.to, (node) => {\r\n if (node.isTextblock && !allowed.has(node.type.name)) {\r\n valid = false\r\n return false // stop early\r\n }\r\n return valid\r\n })\r\n return valid\r\n }\r\n\r\n return false\r\n}\r\n\r\n/**\r\n * Handles image upload with progress tracking and abort capability\r\n * @param file The file to upload\r\n * @param onProgress Optional callback for tracking upload progress\r\n * @param abortSignal Optional AbortSignal for cancelling the upload\r\n * @returns Promise resolving to the URL of the uploaded image\r\n */\r\nexport const handleImageUpload = async (\r\n file: File,\r\n onProgress?: (event: { progress: number }) => void,\r\n abortSignal?: AbortSignal\r\n): Promise<string> => {\r\n // Validate file\r\n if (!file) {\r\n throw new Error(\"No file provided\")\r\n }\r\n\r\n if (file.size > MAX_FILE_SIZE) {\r\n throw new Error(\r\n `File size exceeds maximum allowed (${MAX_FILE_SIZE / (1024 * 1024)}MB)`\r\n )\r\n }\r\n\r\n // For demo/testing: Simulate upload progress. In production, replace the following code\r\n // with your own upload implementation.\r\n for (let progress = 0; progress <= 100; progress += 10) {\r\n if (abortSignal?.aborted) {\r\n throw new Error(\"Upload cancelled\")\r\n }\r\n await new Promise((resolve) => setTimeout(resolve, 500))\r\n onProgress?.({ progress })\r\n }\r\n\r\n const objectUrl = URL.createObjectURL(file);\r\n\r\n return objectUrl;\r\n}\r\n\r\ntype ProtocolOptions = {\r\n /**\r\n * The protocol scheme to be registered.\r\n * @default '''\r\n * @example 'ftp'\r\n * @example 'git'\r\n */\r\n scheme: string\r\n\r\n /**\r\n * If enabled, it allows optional slashes after the protocol.\r\n * @default false\r\n * @example true\r\n */\r\n optionalSlashes?: boolean\r\n}\r\n\r\ntype ProtocolConfig = Array<ProtocolOptions | string>\r\n\r\nconst ATTR_WHITESPACE =\r\n // eslint-disable-next-line no-control-regex\r\n /[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]/g\r\n\r\nexport function isAllowedUri(\r\n uri: string | undefined,\r\n protocols?: ProtocolConfig\r\n) {\r\n const allowedProtocols: string[] = [\r\n \"http\",\r\n \"https\",\r\n \"ftp\",\r\n \"ftps\",\r\n \"mailto\",\r\n \"tel\",\r\n \"callto\",\r\n \"sms\",\r\n \"cid\",\r\n \"xmpp\",\r\n ]\r\n\r\n if (protocols) {\r\n protocols.forEach((protocol) => {\r\n const nextProtocol =\r\n typeof protocol === \"string\" ? protocol : protocol.scheme\r\n\r\n if (nextProtocol) {\r\n allowedProtocols.push(nextProtocol)\r\n }\r\n })\r\n }\r\n\r\n return (\r\n !uri ||\r\n uri.replace(ATTR_WHITESPACE, \"\").match(\r\n new RegExp(\r\n // eslint-disable-next-line no-useless-escape\r\n `^(?:(?:${allowedProtocols.join(\"|\")}):|[^a-z]|[a-z0-9+.\\-]+(?:[^a-z+.\\-:]|$))`,\r\n \"i\"\r\n )\r\n )\r\n )\r\n}\r\n\r\nexport function sanitizeUrl(\r\n inputUrl: string,\r\n baseUrl: string,\r\n protocols?: ProtocolConfig\r\n): string {\r\n try {\r\n const url = new URL(inputUrl, baseUrl)\r\n\r\n if (isAllowedUri(url.href, protocols)) {\r\n return url.href\r\n }\r\n } catch {\r\n // If URL creation fails, it's considered invalid\r\n }\r\n return \"#\"\r\n}\r\n\r\n/**\r\n * Update a single attribute on multiple nodes.\r\n *\r\n * @param tr - The transaction to mutate\r\n * @param targets - Array of { node, pos }\r\n * @param attrName - Attribute key to update\r\n * @param next - New value OR updater function receiving previous value\r\n * Pass `undefined` to remove the attribute.\r\n * @returns true if at least one node was updated, false otherwise\r\n */\r\nexport function updateNodesAttr<A extends string = string, V = unknown>(\r\n tr: Transaction,\r\n targets: readonly NodeWithPos[],\r\n attrName: A,\r\n next: V | ((prev: V | undefined) => V | undefined)\r\n): boolean {\r\n if (!targets.length) return false\r\n\r\n let changed = false\r\n\r\n for (const { pos } of targets) {\r\n // Always re-read from the transaction's current doc\r\n const currentNode = tr.doc.nodeAt(pos)\r\n if (!currentNode) continue\r\n\r\n const prevValue = (currentNode.attrs as Record<string, unknown>)[\r\n attrName\r\n ] as V | undefined\r\n const resolvedNext =\r\n typeof next === \"function\"\r\n ? (next as (p: V | undefined) => V | undefined)(prevValue)\r\n : next\r\n\r\n if (prevValue === resolvedNext) continue\r\n\r\n const nextAttrs: Record<string, unknown> = { ...currentNode.attrs }\r\n if (resolvedNext === undefined) {\r\n // Remove the key entirely instead of setting null\r\n delete nextAttrs[attrName]\r\n } else {\r\n nextAttrs[attrName] = resolvedNext\r\n }\r\n\r\n tr.setNodeMarkup(pos, undefined, nextAttrs)\r\n changed = true\r\n }\r\n\r\n return changed\r\n}\r\n\r\n/**\r\n * Selects the entire content of the current block node if the selection is empty.\r\n * If the selection is not empty, it does nothing.\r\n * @param editor The Tiptap editor instance\r\n */\r\nexport function selectCurrentBlockContent(editor: Editor) {\r\n const { selection, doc } = editor.state\r\n\r\n if (!selection.empty) return\r\n\r\n const $pos = selection.$from\r\n let blockNode = null\r\n let blockPos = -1\r\n\r\n for (let depth = $pos.depth; depth >= 0; depth--) {\r\n const node = $pos.node(depth)\r\n const pos = $pos.start(depth)\r\n\r\n if (node.isBlock && node.textContent.trim()) {\r\n blockNode = node\r\n blockPos = pos\r\n break\r\n }\r\n }\r\n\r\n if (blockNode && blockPos >= 0) {\r\n const from = blockPos\r\n const to = blockPos + blockNode.nodeSize - 2 // -2 to exclude the closing tag\r\n\r\n if (from < to) {\r\n const $from = doc.resolve(from)\r\n const $to = doc.resolve(to)\r\n const newSelection = TextSelection.between($from, $to, 1)\r\n\r\n if (newSelection && !selection.eq(newSelection)) {\r\n editor.view.dispatch(editor.state.tr.setSelection(newSelection))\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves all nodes of specified types from the current selection.\r\n * @param selection The current editor selection\r\n * @param allowedNodeTypes An array of node type names to look for (e.g., [\"image\", \"table\"])\r\n * @returns An array of objects containing the node and its position\r\n */\r\nexport function getSelectedNodesOfType(\r\n selection: Selection,\r\n allowedNodeTypes: string[]\r\n): NodeWithPos[] {\r\n const results: NodeWithPos[] = []\r\n const allowed = new Set(allowedNodeTypes)\r\n\r\n if (selection instanceof CellSelection) {\r\n selection.forEachCell((node: PMNode, pos: number) => {\r\n if (allowed.has(node.type.name)) {\r\n results.push({ node, pos })\r\n }\r\n })\r\n return results\r\n }\r\n\r\n if (selection instanceof NodeSelection) {\r\n const { node, from: pos } = selection\r\n if (node && allowed.has(node.type.name)) {\r\n results.push({ node, pos })\r\n }\r\n return results\r\n }\r\n\r\n const { $anchor } = selection\r\n const cell = cellAround($anchor)\r\n\r\n if (cell) {\r\n const cellNode = selection.$anchor.doc.nodeAt(cell.pos)\r\n if (cellNode && allowed.has(cellNode.type.name)) {\r\n results.push({ node: cellNode, pos: cell.pos })\r\n return results\r\n }\r\n }\r\n\r\n // Fallback: find parent nodes of allowed types\r\n const parentNode = findParentNodeClosestToPos($anchor, (node) =>\r\n allowed.has(node.type.name)\r\n )\r\n\r\n if (parentNode) {\r\n results.push({ node: parentNode.node, pos: parentNode.pos })\r\n }\r\n\r\n return results\r\n}\r\n\r\n/**\r\n * Clamps a value between min and max bounds\r\n */\r\nexport function clamp(value: number, min: number, max: number): number {\r\n return Math.max(min, Math.min(value, max))\r\n}\r\n\r\nexport function getSelectedBlockNodes(editor: Editor): PMNode[] {\r\n const { doc } = editor.state\r\n const { from, to } = editor.state.selection\r\n\r\n const blocks: PMNode[] = []\r\n const seen = new Set<number>()\r\n\r\n doc.nodesBetween(from, to, (node, pos) => {\r\n if (!node.isBlock) return\r\n\r\n if (!seen.has(pos)) {\r\n seen.add(pos)\r\n blocks.push(node)\r\n }\r\n\r\n return false\r\n })\r\n\r\n return blocks\r\n}\r\n","import { CloudUploadIcon, FileImage } from \"lucide-react\";\nimport { useMessages } from \"~/i18n\";\n\nexport const DropZoneContent: React.FC<{ maxSize: number; limit: number }> = ({\n maxSize,\n limit,\n}) => {\n const t = useMessages();\n return (\n <>\n <div className=\"tiptap-image-upload-dropzone\">\n <FileImage size={50} className=\"tiptap-image-upload-dropzone-rect-primary\" />\n <div className=\"tiptap-image-upload-icon-container\">\n <CloudUploadIcon className=\"tiptap-image-upload-icon\" />\n </div>\n </div>\n\n <div className=\"tiptap-image-upload-content\">\n <span className=\"tiptap-image-upload-text\">\n <em>{t.image.clickToUpload}</em> {t.image.orDragAndDrop}\n </span>\n <span className=\"tiptap-image-upload-subtext\">\n {t.image.limitHint(limit, maxSize / 1024 / 1024)}\n </span>\n </div>\n </>\n );\n};\n","import { useState } from \"react\";\r\n\r\ninterface ImageUploadDragAreaProps {\r\n /**\r\n * Callback function triggered when files are dropped or selected\r\n * @param {File[]} files - Array of File objects that were dropped or selected\r\n */\r\n onFile: (files: File[]) => void;\r\n /**\r\n * Optional child elements to render inside the drag area\r\n * @optional\r\n * @default undefined\r\n */\r\n children?: React.ReactNode;\r\n}\r\n\r\nexport const ImageUploadDragArea: React.FC<ImageUploadDragAreaProps> = ({\r\n onFile,\r\n children,\r\n}) => {\r\n const [isDragOver, setIsDragOver] = useState(false);\r\n const [isDragActive, setIsDragActive] = useState(false);\r\n\r\n const handleDragEnter = (e: React.DragEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n setIsDragActive(true);\r\n };\r\n\r\n const handleDragLeave = (e: React.DragEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n if (!e.currentTarget.contains(e.relatedTarget as Node)) {\r\n setIsDragActive(false);\r\n setIsDragOver(false);\r\n }\r\n };\r\n\r\n const handleDragOver = (e: React.DragEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n setIsDragOver(true);\r\n };\r\n\r\n const handleDrop = (e: React.DragEvent) => {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n setIsDragActive(false);\r\n setIsDragOver(false);\r\n\r\n const files = Array.from(e.dataTransfer.files);\r\n if (files.length > 0) {\r\n onFile(files);\r\n }\r\n };\r\n\r\n return (\r\n <div\r\n className={`tiptap-image-upload-drag-area ${isDragActive ? \"drag-active\" : \"\"} ${isDragOver ? \"drag-over\" : \"\"}`}\r\n onDragEnter={handleDragEnter}\r\n onDragLeave={handleDragLeave}\r\n onDragOver={handleDragOver}\r\n onDrop={handleDrop}\r\n >\r\n {children}\r\n </div>\r\n );\r\n};\r\n","import { CloudUploadIcon, XIcon } from \"lucide-react\";\r\nimport { Button } from \"~/components/ui/button\";\r\nimport { FileItem } from \"./types\";\r\n\r\ninterface ImageUploadPreviewProps {\r\n /**\r\n * The file item to preview\r\n */\r\n fileItem: FileItem;\r\n /**\r\n * Callback to remove this file from upload queue\r\n */\r\n onRemove: () => void;\r\n}\r\n\r\nexport const ImageUploadPreview: React.FC<ImageUploadPreviewProps> = ({\r\n fileItem,\r\n onRemove,\r\n}) => {\r\n const formatFileSize = (bytes: number) => {\r\n if (bytes === 0) return \"0 Bytes\";\r\n const k = 1024;\r\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(k));\r\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\r\n };\r\n\r\n return (\r\n <div className=\"tiptap-image-upload-preview\">\r\n {fileItem.status === \"uploading\" && (\r\n <div\r\n className=\"tiptap-image-upload-progress\"\r\n style={{ width: `${fileItem.progress}%` }}\r\n />\r\n )}\r\n\r\n <div className=\"tiptap-image-upload-preview-content\">\r\n <div className=\"tiptap-image-upload-file-info\">\r\n <div className=\"tiptap-image-upload-file-icon\">\r\n <CloudUploadIcon />\r\n </div>\r\n <div className=\"tiptap-image-upload-details\">\r\n <span className=\"tiptap-image-upload-text\">\r\n {fileItem.file.name}\r\n </span>\r\n <span className=\"tiptap-image-upload-subtext\">\r\n {formatFileSize(fileItem.file.size)}\r\n </span>\r\n </div>\r\n </div>\r\n <div className=\"tiptap-image-upload-actions\">\r\n {fileItem.status === \"uploading\" && (\r\n <span className=\"tiptap-image-upload-progress-text\">\r\n {fileItem.progress}%\r\n </span>\r\n )}\r\n <Button\r\n size={\"icon\"}\r\n type=\"button\"\r\n variant=\"ghost\"\r\n onClick={(e) => {\r\n e.stopPropagation();\r\n onRemove();\r\n }}\r\n >\r\n <XIcon className=\"tiptap-button-icon\" />\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n};\r\n","import { useState } from \"react\";\r\nimport { FileItem, UploadOptions } from \"./types\";\r\n\r\nexport function useFileUpload(options: UploadOptions) {\r\n const [fileItems, setFileItems] = useState<FileItem[]>([]);\r\n\r\n const uploadFile = async (file: File): Promise<string | null> => {\r\n if (file.size > options.maxSize) {\r\n const error = new Error(\r\n `File size exceeds maximum allowed (${options.maxSize / 1024 / 1024}MB)`,\r\n );\r\n options.onError?.(error);\r\n return null;\r\n }\r\n\r\n const abortController = new AbortController();\r\n const fileId = crypto.randomUUID();\r\n\r\n const newFileItem: FileItem = {\r\n id: fileId,\r\n file,\r\n progress: 0,\r\n status: \"uploading\",\r\n abortController,\r\n };\r\n\r\n setFileItems((prev) => [...prev, newFileItem]);\r\n\r\n try {\r\n if (!options.upload) {\r\n throw new Error(\"Upload function is not defined\");\r\n }\r\n\r\n const url = await options.upload(\r\n file,\r\n (event: { progress: number }) => {\r\n setFileItems((prev) =>\r\n prev.map((item) =>\r\n item.id === fileId ? { ...item, progress: event.progress } : item,\r\n ),\r\n );\r\n },\r\n abortController.signal,\r\n );\r\n\r\n console.log(url);\r\n\r\n if (!url) throw new Error(\"Upload failed: No URL returned\");\r\n\r\n if (!abortController.signal.aborted) {\r\n setFileItems((prev) =>\r\n prev.map((item) =>\r\n item.id === fileId\r\n ? { ...item, status: \"success\", url, progress: 100 }\r\n : item,\r\n ),\r\n );\r\n options.onSuccess?.(url);\r\n return url;\r\n }\r\n\r\n return null;\r\n } catch (error) {\r\n if (!abortController.signal.aborted) {\r\n setFileItems((prev) =>\r\n prev.map((item) =>\r\n item.id === fileId\r\n ? { ...item, status: \"error\", progress: 0 }\r\n : item,\r\n ),\r\n );\r\n options.onError?.(\r\n error instanceof Error ? error : new Error(\"Upload failed\"),\r\n );\r\n }\r\n return null;\r\n }\r\n };\r\n\r\n const uploadFiles = async (files: File[]): Promise<string[]> => {\r\n if (!files || files.length === 0) {\r\n options.onError?.(new Error(\"No files to upload\"));\r\n return [];\r\n }\r\n\r\n if (options.limit && files.length > options.limit) {\r\n options.onError?.(\r\n new Error(\r\n `Maximum ${options.limit} file${options.limit === 1 ? \"\" : \"s\"} allowed`,\r\n ),\r\n );\r\n return [];\r\n }\r\n\r\n // Upload all files concurrently\r\n const uploadPromises = files.map((file) => uploadFile(file));\r\n const results = await Promise.all(uploadPromises);\r\n\r\n // Filter out null results (failed uploads)\r\n return results.filter((url): url is string => url !== null);\r\n };\r\n\r\n const removeFileItem = (fileId: string) => {\r\n setFileItems((prev) => {\r\n const fileToRemove = prev.find((item) => item.id === fileId);\r\n if (fileToRemove?.abortController) {\r\n fileToRemove.abortController.abort();\r\n }\r\n if (fileToRemove?.url) {\r\n URL.revokeObjectURL(fileToRemove.url);\r\n }\r\n return prev.filter((item) => item.id !== fileId);\r\n });\r\n };\r\n\r\n const clearAllFiles = () => {\r\n fileItems.forEach((item) => {\r\n if (item.abortController) {\r\n item.abortController.abort();\r\n }\r\n if (item.url) {\r\n URL.revokeObjectURL(item.url);\r\n }\r\n });\r\n setFileItems([]);\r\n };\r\n\r\n return {\r\n fileItems,\r\n uploadFiles,\r\n removeFileItem,\r\n clearAllFiles,\r\n };\r\n}","\"use client\";\n\nimport { useRef } from \"react\";\nimport type { NodeViewProps } from \"@tiptap/react\";\nimport { NodeViewWrapper } from \"@tiptap/react\";\n\nimport {\n focusNextNode,\n handleImageUpload,\n isValidPosition,\n} from \"~/lib/tiptap-utils\";\nimport { Button } from \"~/components/ui/button\";\n\nimport { DropZoneContent } from \"./drop-zone-content\";\nimport { ImageUploadDragArea } from \"./image-drop-area\";\nimport { ImageUploadPreview } from \"./image-preview\";\nimport { UploadOptions } from \"./types\";\nimport { useFileUpload } from \"./hooks\";\nimport { useMessages } from \"~/i18n\";\n\nexport const ImageUploadNode: React.FC<NodeViewProps> = (props) => {\n const t = useMessages();\n const { accept, limit, maxSize } = props.node.attrs;\n\n const inputRef = useRef<HTMLInputElement>(null);\n const extension = props.extension;\n\n const uploadOptions: UploadOptions = {\n maxSize,\n limit,\n accept,\n upload: handleImageUpload,\n onSuccess: extension.options.onSuccess,\n onError: extension.options.onError,\n };\n\n const { fileItems, uploadFiles, removeFileItem, clearAllFiles } =\n useFileUpload(uploadOptions);\n\n const handleUpload = async (files: File[]) => {\n const urls = await uploadFiles(files);\n\n if (urls.length > 0) {\n const pos = props.getPos();\n\n if (isValidPosition(pos)) {\n const imageNodes = urls.map((url, index) => {\n const filename =\n files[index]?.name.replace(/\\.[^/.]+$/, \"\") || \"unknown\";\n\n return {\n type: extension.options.type,\n attrs: {\n ...extension.options,\n src: url,\n alt: filename,\n title: filename,\n },\n };\n });\n\n props.editor\n .chain()\n .focus()\n .deleteRange({ from: pos, to: pos + props.node.nodeSize })\n .insertContentAt(pos, imageNodes[0])\n .run();\n\n focusNextNode(props.editor);\n }\n }\n };\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n if (!files || files.length === 0) {\n extension.options.onError?.(new Error(\"No file selected\"));\n return;\n }\n handleUpload(Array.from(files));\n };\n\n const handleClick = () => {\n if (inputRef.current && fileItems.length === 0) {\n inputRef.current.value = \"\";\n inputRef.current.click();\n }\n };\n\n const hasFiles = fileItems.length > 0;\n\n\n return (\n <NodeViewWrapper\n className=\"tiptap-image-upload py-8\"\n tabIndex={0}\n onClick={handleClick}\n >\n {!hasFiles && (\n <ImageUploadDragArea onFile={handleUpload}>\n <DropZoneContent maxSize={maxSize} limit={limit} />\n </ImageUploadDragArea>\n )}\n\n {hasFiles && (\n <div className=\"tiptap-image-upload-previews\">\n {fileItems.length > 1 && (\n <div className=\"tiptap-image-upload-header\">\n <span>Uploading {fileItems.length} files</span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n onClick={(e) => {\n e.stopPropagation();\n clearAllFiles();\n }}\n >\n {t.image.clearAll}\n </Button>\n </div>\n )}\n {fileItems.map((fileItem) => (\n <ImageUploadPreview\n key={fileItem.id}\n fileItem={fileItem}\n onRemove={() => removeFileItem(fileItem.id)}\n />\n ))}\n </div>\n )}\n\n <input\n ref={inputRef}\n name=\"file\"\n accept={accept}\n type=\"file\"\n multiple={limit > 1}\n onChange={handleChange}\n onClick={(e: React.MouseEvent<HTMLInputElement>) => e.stopPropagation()}\n />\n </NodeViewWrapper>\n );\n};\n","import { mergeAttributes, Node } from \"@tiptap/react\"\r\nimport { ReactNodeViewRenderer } from \"@tiptap/react\"\r\nimport { ImageUploadNode as ImageUploadNodeComponent } from \"./image-upload-node-view\"\r\nimport type { NodeType } from \"@tiptap/pm/model\"\r\n\r\nexport type UploadFunction = (\r\n file: File,\r\n onProgress?: (event: { progress: number }) => void,\r\n abortSignal?: AbortSignal\r\n) => Promise<string>\r\n\r\nexport interface ImageUploadNodeOptions {\r\n /**\r\n * The type of the node.\r\n * @default 'image'\r\n */\r\n type?: string | NodeType | undefined\r\n /**\r\n * Acceptable file types for upload.\r\n * @default 'image/*'\r\n */\r\n accept?: string\r\n /**\r\n * Maximum number of files that can be uploaded.\r\n * @default 1\r\n */\r\n limit?: number\r\n /**\r\n * Maximum file size in bytes (0 for unlimited).\r\n * @default 0\r\n */\r\n maxSize?: number\r\n /**\r\n * Function to handle the upload process.\r\n */\r\n upload?: UploadFunction\r\n /**\r\n * Callback for upload errors.\r\n */\r\n onError?: (error: Error) => void\r\n /**\r\n * Callback for successful uploads.\r\n */\r\n onSuccess?: (url: string) => void\r\n /**\r\n * HTML attributes to add to the image element.\r\n * @default {}\r\n * @example { class: 'foo' }\r\n */\r\n HTMLAttributes: Record<string, unknown>\r\n}\r\n\r\ndeclare module \"@tiptap/react\" {\r\n interface Commands<ReturnType> {\r\n imageUpload: {\r\n setImageUploadNode: (options?: ImageUploadNodeOptions) => ReturnType\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * A Tiptap node extension that creates an image upload component.\r\n * @see registry/tiptap-node/image-upload-node/image-upload-node\r\n */\r\nexport const ImageUploadNode = Node.create<ImageUploadNodeOptions>({\r\n name: \"imageUpload\",\r\n group: \"block\",\r\n draggable: true,\r\n selectable: true,\r\n atom: true,\r\n\r\n addOptions() {\r\n return {\r\n type: \"image\",\r\n accept: \"image/*\",\r\n limit: 1,\r\n maxSize: 1048576,\r\n upload: undefined,\r\n onError: undefined,\r\n onSuccess: undefined,\r\n HTMLAttributes: {},\r\n }\r\n },\r\n\r\n addAttributes() {\r\n return {\r\n accept: {\r\n default: this.options.accept,\r\n },\r\n limit: {\r\n default: this.options.limit,\r\n },\r\n maxSize: {\r\n default: this.options.maxSize,\r\n },\r\n }\r\n },\r\n\r\n parseHTML() {\r\n return [{ tag: 'div[data-type=\"image-upload\"]' }]\r\n },\r\n\r\n renderHTML({ HTMLAttributes }) {\r\n return [\r\n \"div\",\r\n mergeAttributes({ \"data-type\": \"image-upload\" }, HTMLAttributes),\r\n ]\r\n },\r\n\r\n addNodeView() {\r\n return ReactNodeViewRenderer(ImageUploadNodeComponent)\r\n },\r\n\r\n addCommands() {\r\n return {\r\n setImageUploadNode:\r\n (options) =>\r\n ({ commands }) => {\r\n return commands.insertContent({\r\n type: this.name,\r\n attrs: options,\r\n })\r\n },\r\n }\r\n },\r\n\r\n /**\r\n * Adds Enter key handler to trigger the upload component when it's selected.\r\n */\r\n addKeyboardShortcuts() {\r\n return {\r\n Enter: ({ editor }) => {\r\n const { selection } = editor.state\r\n const { nodeAfter } = selection.$from\r\n\r\n if (\r\n nodeAfter &&\r\n nodeAfter.type.name === \"imageUpload\" &&\r\n editor.isActive(\"imageUpload\")\r\n ) {\r\n const nodeEl = editor.view.nodeDOM(selection.$from.pos)\r\n if (nodeEl && nodeEl instanceof HTMLElement) {\r\n // Since NodeViewWrapper is wrapped with a div, we need to click the first child\r\n const firstChild = nodeEl.firstChild\r\n if (firstChild && firstChild instanceof HTMLElement) {\r\n firstChild.click()\r\n return true\r\n }\r\n }\r\n }\r\n return false\r\n },\r\n }\r\n },\r\n})\r\n\r\nexport default ImageUploadNode\r\n","import { mergeAttributes } from \"@tiptap/core\";\nimport { createColGroup, Table } from \"@tiptap/extension-table\";\nimport { DOMOutputSpec, DOMSerializer } from \"@tiptap/pm/model\";\nimport { cn } from \"~/lib/utils\";\n\nexport const CustomTable = Table.extend({\n renderHTML({ node, HTMLAttributes }) {\n const { colgroup, tableWidth, tableMinWidth } = createColGroup(node, this.options.cellMinWidth);\n\n const table: DOMOutputSpec = [\n \"div\",\n {\n class: cn(\"table-wrapper overflow-x-auto overflow-y-hidden p-4 -ml-4 relative\")\n },\n \"table\",\n mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n style: tableWidth ? `width: ${tableWidth}` : `min-width: ${tableMinWidth}`\n }),\n colgroup,\n ['table', 0]\n ]\n return table;\n },\n addNodeView() {\n return ({ node, HTMLAttributes }) => {\n const { colgroup, tableWidth, tableMinWidth } = createColGroup(node, this.options.cellMinWidth)\n\n const dom = document.createElement(\"div\");\n dom.setAttribute('data-content-type', 'table');\n // dom.className = cn('mb-4');\n const wrapper = document.createElement(\"div\");\n wrapper.className = cn(\"table-wrapper overflow-x-auto overflow-y-hidden p-4 -ml-4 relative\")\n\n const tableContainer = document.createElement(\"div\");\n tableContainer.className = \"table-container\";\n\n const table = document.createElement('table');\n\n Object.entries(this.options.HTMLAttributes).forEach(([key, value]) => {\n table.setAttribute(key, value)\n })\n\n Object.entries(HTMLAttributes).forEach(([key, value]) => {\n table.setAttribute(key, value)\n })\n\n table.style.width = tableWidth;\n // Phải khớp cellMinWidth trong table-kit: createColGroup dùng số đó\n // cho <col>, còn table.css lại ép min-width của ô theo biến này.\n // Lệch nhau thì cột và ô mỗi bên tính một kiểu.\n table.style.setProperty('--default-cell-min-width', '152px');\n\n const colGroup = DOMSerializer.renderSpec(document, colgroup);\n\n const content = document.createElement(\"tbody\");\n table.append(colGroup.dom, content);\n\n tableContainer.append(table);\n\n const tableControls = document.createElement(\"div\");\n tableControls.className = 'table-controls';\n\n const tableSelectionContainer = document.createElement(\"div\");\n tableSelectionContainer.className = 'table-selection-container';\n\n wrapper.append(tableContainer, tableControls, tableSelectionContainer)\n\n dom.append(wrapper);\n\n return {\n dom: dom,\n contentDOM: content,\n ignoreMutation: (_mutation) => {\n return true;\n }\n }\n }\n }\n})","import { Extension } from '@tiptap/core'\r\nimport { TableRow, TableCell, TableHeader } from '@tiptap/extension-table'\r\nimport { CustomTable } from './CustomTable'\r\nimport { cn } from '~/lib/utils'\r\n\r\nexport const CustomTableKit = Extension.create({\r\n name: 'custom-table-kit',\r\n\r\n\r\n addExtensions() {\r\n return [\r\n CustomTable.configure({\r\n HTMLAttributes: {\r\n class: cn(\"border-collapse w-full not-prose\"),\r\n },\r\n lastColumnResizable: false,\r\n allowTableNodeSelection: true,\r\n resizable: true,\r\n cellMinWidth: 152\r\n }),\r\n TableHeader.extend({\r\n addAttributes() {\r\n return {\r\n ...this.parent?.(),\r\n background: {\r\n default: null,\r\n parseHTML: element => element.style.backgroundColor || null,\r\n renderHTML: attributes => {\r\n if (!attributes.background) return {}\r\n return { style: `background-color: ${attributes.background}` }\r\n },\r\n },\r\n alignment: {\r\n default: 'left',\r\n parseHTML: element => element.style.textAlign || 'left',\r\n renderHTML: attributes => {\r\n if (!attributes.alignment) return {}\r\n return { style: `text-align: ${attributes.alignment}` }\r\n },\r\n },\r\n verticalAlign: {\r\n default: 'top',\r\n parseHTML: (element) => {\r\n return element.style.verticalAlign || \"top\"\r\n },\r\n renderHTML: (attributes) => {\r\n return { style: `vertical-align: ${attributes.verticalAlign}` }\r\n }\r\n }\r\n }\r\n }\r\n }).configure({\r\n HTMLAttributes: {\r\n class: cn(\r\n \"border border-border p-2 text-start font-semibold\"\r\n ),\r\n },\r\n }),\r\n TableRow,\r\n TableCell.extend({\r\n addAttributes() {\r\n return {\r\n ...this.parent?.(),\r\n background: {\r\n default: null,\r\n parseHTML: element => element.style.backgroundColor || null,\r\n renderHTML: attributes => {\r\n if (!attributes.background) return {}\r\n return { style: `background-color: ${attributes.background}` }\r\n },\r\n },\r\n alignment: {\r\n default: 'left',\r\n parseHTML: element => element.style.textAlign || 'left',\r\n renderHTML: attributes => {\r\n if (!attributes.alignment) return {}\r\n return { style: `text-align: ${attributes.alignment}` }\r\n },\r\n },\r\n verticalAlign: {\r\n default: 'top',\r\n parseHTML: (element) => {\r\n return element.style.verticalAlign || \"top\"\r\n },\r\n renderHTML: (attributes) => {\r\n return { style: `vertical-align: ${attributes.verticalAlign}` }\r\n }\r\n }\r\n }\r\n }\r\n }).configure({\r\n HTMLAttributes: {\r\n class: \"border border-border p-2\"\r\n }\r\n }),\r\n\r\n ]\r\n },\r\n})","import type { NodeViewProps } from \"@tiptap/react\";\nimport { NodeViewWrapper, useEditorState } from \"@tiptap/react\";\nimport type { TableOfContentDataItem } from \"@tiptap/extension-table-of-contents\";\nimport TocSidebarContent from \"../../toc-sidebar/TocSidebarContent\";\n\n/**\n * Mục lục nhúng thẳng trong nội dung.\n *\n * Dữ liệu lấy từ storage của extension TableOfContents — cùng nguồn với thanh\n * mục lục ở lề, nên hai chỗ không bao giờ lệch nhau.\n */\nexport function TocNodeView({ editor }: NodeViewProps) {\n\tconst items = useEditorState({\n\t\teditor,\n\t\tselector: (ctx) =>\n\t\t\t(ctx.editor.storage.tableOfContents?.content ??\n\t\t\t\t[]) as TableOfContentDataItem[],\n\t});\n\n\treturn (\n\t\t<NodeViewWrapper\n\t\t\tclassName=\"not-prose my-4 rounded-lg border border-border p-2\"\n\t\t\tdata-type=\"table-of-contents\"\n\t\t>\n\t\t\t{/* contentEditable={false}: nếu không, con trỏ chui vào được bên trong\n\t\t\t mục lục và người dùng gõ đè lên một khối chỉ để đọc. */}\n\t\t\t<div contentEditable={false}>\n\t\t\t\t<TocSidebarContent items={items} scrollOver={false} />\n\t\t\t</div>\n\t\t</NodeViewWrapper>\n\t);\n}\n\nexport default TocNodeView;\n","import { mergeAttributes, Node } from \"@tiptap/core\";\nimport { ReactNodeViewRenderer } from \"@tiptap/react\";\nimport { TocNodeView } from \"./TocNodeView\";\n\n/**\n * Node cho mục lục nhúng trong nội dung.\n *\n * `@tiptap/extension-table-of-contents` chỉ là Extension: nó tính toán danh\n * sách heading rồi để vào `editor.storage.tableOfContents`, chứ không đăng ký\n * node nào. Muốn chèn được mục lục vào document thì phải có node riêng.\n *\n * Tên phải khác `tableOfContents` — đó là tên của chính Extension kia, trùng\n * tên sẽ tranh nhau khi Tiptap dựng schema.\n */\nexport const TableOfContentsNode = Node.create({\n\tname: \"tableOfContentsNode\",\n\tgroup: \"block\",\n\tatom: true,\n\tselectable: true,\n\tdraggable: true,\n\n\tparseHTML() {\n\t\treturn [{ tag: 'div[data-type=\"table-of-contents\"]' }];\n\t},\n\n\trenderHTML({ HTMLAttributes }) {\n\t\treturn [\n\t\t\t\"div\",\n\t\t\tmergeAttributes({ \"data-type\": \"table-of-contents\" }, HTMLAttributes),\n\t\t];\n\t},\n\n\taddNodeView() {\n\t\treturn ReactNodeViewRenderer(TocNodeView);\n\t},\n});\n\nexport default TableOfContentsNode;\n","import FileHandler from \"@tiptap/extension-file-handler\";\nimport type { Editor } from \"@tiptap/core\";\nimport type { UploadFunction } from \"../node/image-upload/image-upload-node-extension\";\nimport type { AttachmentUploadFunction } from \"../node/attachment\";\n\ntype Handlers = {\n\t/** Ảnh: chèn thành node `image` để xem trực tiếp. */\n\tuploadImage?: UploadFunction;\n\t/** Tệp khác: chèn thành node `attachment` để tải về. */\n\tuploadAttachment?: AttachmentUploadFunction;\n};\n\nconst isImage = (file: File) => file.type.startsWith(\"image/\");\n\n/**\n * Xử lý tệp được kéo-thả hoặc dán vào editor.\n *\n * Không giới hạn `allowedMimeTypes` nữa: bản trước chỉ nhận bốn định dạng ảnh,\n * nên kéo một file Word vào editor thì trình duyệt mở file đó thay vì chèn.\n *\n * Ảnh và tệp thường đi hai đường khác nhau vì hiển thị khác nhau, nhưng cả hai\n * đều chỉ nhận URL do app chủ trả về.\n */\nexport function createFileHandler({ uploadImage, uploadAttachment }: Handlers) {\n\tconst insert = async (editor: Editor, file: File, pos: number) => {\n\t\tconst image = isImage(file);\n\t\tconst upload = image ? uploadImage : uploadAttachment;\n\n\t\tif (!upload) {\n\t\t\tif (!image) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[notion-editor] chưa cấu hình `uploadAttachment`, bỏ qua tệp:\",\n\t\t\t\t\tfile.name,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Ảnh không có uploader thì rơi về base64 như trước: tiện để chạy thử,\n\t\t\t// nhưng làm document phình rất nhanh nên đừng dùng cho nội dung lưu lại.\n\t\t\tconst reader = new FileReader();\n\t\t\treader.onload = () => {\n\t\t\t\teditor\n\t\t\t\t\t.chain()\n\t\t\t\t\t.insertContentAt(pos, { type: \"image\", attrs: { src: reader.result } })\n\t\t\t\t\t.focus()\n\t\t\t\t\t.run();\n\t\t\t};\n\t\t\treader.readAsDataURL(file);\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst uploaded = await upload(file);\n\t\t\t// Ảnh luôn cần URL để hiển thị; tệp đính kèm thì có thể chỉ có id vì\n\t\t\t// storage riêng tư không phát URL cố định.\n\t\t\tconst ref =\n\t\t\t\ttypeof uploaded === \"string\"\n\t\t\t\t\t? { src: uploaded, fileId: null }\n\t\t\t\t\t: { src: uploaded.src ?? null, fileId: uploaded.fileId ?? null };\n\t\t\teditor\n\t\t\t\t.chain()\n\t\t\t\t.insertContentAt(\n\t\t\t\t\tpos,\n\t\t\t\t\timage\n\t\t\t\t\t\t? { type: \"image\", attrs: { src: ref.src } }\n\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\ttype: \"attachment\",\n\t\t\t\t\t\t\t\tattrs: {\n\t\t\t\t\t\t\t\t\t...ref,\n\t\t\t\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\t\t\t\tsize: file.size,\n\t\t\t\t\t\t\t\t\tmime: file.type,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\t.focus()\n\t\t\t\t.run();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[notion-editor] chèn tệp thất bại:\", error);\n\t\t}\n\t};\n\n\treturn FileHandler.configure({\n\t\tonDrop: (editor, files, pos) => {\n\t\t\tfor (const file of files) void insert(editor, file, pos);\n\t\t},\n\t\tonPaste: (editor, files, htmlContent) => {\n\t\t\t// Có htmlContent nghĩa là nội dung đi kèm markup đã được extension khác\n\t\t\t// dựng lại — chèn thêm ở đây sẽ ra bản trùng.\n\t\t\tif (htmlContent) return false;\n\t\t\tfor (const file of files) {\n\t\t\t\tvoid insert(editor, file, editor.state.selection.anchor);\n\t\t\t}\n\t\t},\n\t});\n}\n","import { Extension } from '@tiptap/core';\r\n\r\nexport const GlobalBlockBackground = Extension.create({\r\n name: 'globalBlockBackground',\r\n\r\n addGlobalAttributes() {\r\n return [\r\n {\r\n types: ['paragraph', 'heading', 'bulletList', 'orderedList', 'listItem', 'table', 'tableCell', 'tableRow', 'blockquote'],\r\n attributes: {\r\n universalBgColor: {\r\n default: null,\r\n parseHTML: element => element.style.backgroundColor || null,\r\n renderHTML: attributes => {\r\n if (!attributes.universalBgColor) return {};\r\n\r\n return {\r\n style: `\r\n background-color: ${attributes.universalBgColor};\r\n width: 100%;\r\n padding: 5px 6px;\r\n box-sizing: border-box;\r\n border-radius: 4px;\r\n `.replace(/\\s+/g, ' ').trim()\r\n };\r\n },\r\n },\r\n },\r\n },\r\n ];\r\n },\r\n});\r\n","import { Extension } from \"@tiptap/core\";\r\n\r\nexport interface IndentOptions {\r\n types: string[]\r\n minLevel: number\r\n maxLevel: number\r\n indentSize: number\r\n}\r\n\r\ndeclare module '@tiptap/core' {\r\n interface Commands<ReturnType> {\r\n indent: {\r\n indent: () => ReturnType\r\n outdent: () => ReturnType\r\n }\r\n }\r\n}\r\n\r\nexport const Indent = Extension.create({\r\n name: 'indent',\r\n addOptions() {\r\n return {\r\n types: ['paragraph', 'heading'],\r\n minLevel: 0,\r\n maxLevel: 10,\r\n indentSize: 24\r\n }\r\n },\r\n addGlobalAttributes() {\r\n return [\r\n {\r\n types: this.options.types,\r\n attributes: {\r\n // This maps to your CSS [data-indent] selector\r\n 'data-indent': {\r\n default: null,\r\n parseHTML: element => element.hasAttribute('data-indent') ? '' : null,\r\n renderHTML: attributes => {\r\n if (!attributes['data-indent-level'] || attributes['data-indent-level'] <= this.options.minLevel) {\r\n return {}\r\n }\r\n return { 'data-indent': '' }\r\n },\r\n },\r\n // This maps to your CSS --tt-indent-level variable\r\n 'data-indent-level': {\r\n default: 0,\r\n parseHTML: element => {\r\n const level = element.getAttribute('data-indent-level')\r\n return level ? parseInt(level, 10) : 0\r\n },\r\n renderHTML: attributes => {\r\n if (!attributes['data-indent-level'] || attributes['data-indent-level'] <= this.options.minLevel) {\r\n return {}\r\n }\r\n return {\r\n 'data-indent-level': attributes['data-indent-level'],\r\n // Inject the CSS variable inline so your calc() logic picks it up\r\n style: `--tt-indent-level: ${attributes['data-indent-level']};`\r\n }\r\n },\r\n },\r\n },\r\n },\r\n ]\r\n },\r\n\r\n addCommands() {\r\n const setIndentLevel = (tr: any, dispatch: any, levelUpdate: number) => {\r\n if (!dispatch) return true\r\n\r\n const { selection } = tr\r\n const { from, to } = selection\r\n let isChanged = false\r\n\r\n tr.doc.nodesBetween(from, to, (node: any, pos: number) => {\r\n if (this.options.types.includes(node.type.name)) {\r\n const currentIndent = node.attrs['data-indent-level'] || 0\r\n let newIndent = currentIndent + levelUpdate\r\n\r\n if (newIndent < this.options.minLevel) newIndent = this.options.minLevel\r\n if (newIndent > this.options.maxLevel) newIndent = this.options.maxLevel\r\n\r\n if (newIndent !== currentIndent) {\r\n tr.setNodeMarkup(pos, undefined, {\r\n ...node.attrs,\r\n 'data-indent': newIndent > 0 ? '' : null,\r\n 'data-indent-level': newIndent,\r\n })\r\n isChanged = true\r\n }\r\n return false\r\n }\r\n return true\r\n })\r\n\r\n if (isChanged) {\r\n dispatch(tr)\r\n }\r\n return isChanged\r\n }\r\n\r\n return {\r\n indent: () => ({ tr, dispatch }) => setIndentLevel(tr, dispatch, 1),\r\n outdent: () => ({ tr, dispatch }) => setIndentLevel(tr, dispatch, -1),\r\n }\r\n },\r\n addKeyboardShortcuts() {\r\n return {\r\n Tab: () => this.editor.commands.indent(),\r\n 'Shift-Tab': () => this.editor.commands.outdent(),\r\n Backspace: () => {\r\n const { selection } = this.editor.state;\r\n const { empty, $anchor } = selection;\r\n\r\n if (!empty) {\r\n return false\r\n }\r\n\r\n if ($anchor.parentOffset !== 0) {\r\n return false;\r\n }\r\n\r\n const currentIndent = $anchor.parent.attrs['data-indent-level'] || 0\r\n if (currentIndent <= this.options.minLevel) {\r\n return false\r\n }\r\n\r\n\r\n return this.editor.commands.outdent()\r\n }\r\n }\r\n },\r\n})","import { EmojiItem } from \"@tiptap/extension-emoji\";\r\nimport { useMemo, useRef } from \"react\";\r\nimport TooltipWraper from \"~/components/TooltipWrapper\";\r\nimport { Button } from \"~/components/ui/button\";\r\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\r\n\r\ninterface EmojiListProps {\r\n items: EmojiItem[];\r\n selectedIndex: number;\r\n onSelectIndex: (index: number) => void;\r\n}\r\n\r\nconst EmojiList = ({ items, onSelectIndex }: EmojiListProps) => {\r\n const parentRef = useRef<HTMLDivElement>(null);\r\n const COLUMNS = 6;\r\n const BUTTON_SIZE = 32; \r\n const GAP = 2;\r\n\r\n const rows = useMemo(() => {\r\n const result: EmojiItem[][] = [];\r\n for (let i = 0; i < items.length; i += COLUMNS) {\r\n result.push(items.slice(i, i + COLUMNS));\r\n }\r\n return result;\r\n }, [items]);\r\n\r\n const rowVirtualizer = useVirtualizer({\r\n count: rows.length,\r\n getScrollElement: () => parentRef.current,\r\n estimateSize: () => BUTTON_SIZE + GAP, \r\n overscan: 5,\r\n });\r\n return (\r\n <div\r\n ref={parentRef}\r\n className=\"overflow-y-auto select-none scrollbar-none\"\r\n style={{\r\n height: '260px', \r\n width: '202px', \r\n }}\r\n >\r\n <div\r\n className=\"w-full relative\"\r\n style={{ height: `${rowVirtualizer.getTotalSize()}px` }}\r\n >\r\n {rowVirtualizer.getVirtualItems().map((virtualRow) => {\r\n const rowItems = rows[virtualRow.index];\r\n\r\n return (\r\n <div\r\n key={virtualRow.key}\r\n data-index={virtualRow.index}\r\n className=\"absolute top-0 left-0 w-full grid grid-cols-6\"\r\n style={{\r\n height: `${BUTTON_SIZE}px`,\r\n gap: `${GAP}px`,\r\n transform: `translateY(${virtualRow.start}px)`,\r\n }}\r\n >\r\n {rowItems.map((emoji, colIdx) => {\r\n const originalIndex = virtualRow.index * COLUMNS + colIdx;\r\n\r\n return (\r\n <TooltipWraper content={emoji.name} key={emoji.name}>\r\n <Button\r\n variant=\"secondary\"\r\n onClick={() => onSelectIndex(originalIndex)}\r\n size=\"icon\"\r\n style={{ width: `${BUTTON_SIZE}px`, height: `${BUTTON_SIZE}px` }}\r\n className=\"p-0 flex items-center justify-center shrink-0\"\r\n >\r\n {/* 1949/1968 emoji có cả ký tự unicode lẫn fallbackImage. Ưu tiên\r\n ảnh nghĩa là mọi ô đều phải tải từ cdn.jsdelivr.net, mạng chặn\r\n hay offline là vỡ hết. Ảnh chỉ dành cho ~19 emoji riêng của\r\n GitHub vốn không có ký tự unicode. */}\r\n {emoji.emoji ? (\r\n <span className=\"text-base leading-none\">{emoji.emoji}</span>\r\n ) : (\r\n <img\r\n className=\"w-4 h-4\"\r\n src={emoji.fallbackImage}\r\n alt={emoji.name}\r\n />\r\n )}\r\n </Button>\r\n </TooltipWraper>\r\n );\r\n })}\r\n </div>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n );\r\n};\r\n\r\nexport default EmojiList;\r\n\r\n// <div ref={parentRef} className=\" grid grid-cols-6 \">\r\n// {items.map((emoji, idx) => {\r\n\r\n// return (\r\n// <TooltipWraper content={emoji.name}>\r\n// <Button\r\n// variant={\"secondary\"}\r\n// key={emoji.name}\r\n// onClick={() => onSelectIndex(idx)}\r\n// className=\"\"\r\n// size={\"icon\"}\r\n// >\r\n// {emoji.fallbackImage ? (\r\n// <img\r\n// className=\"h-4 w-4 flex justify-center items-center\"\r\n// src={emoji.fallbackImage}\r\n// alt={emoji.name}\r\n// />\r\n// ) : (\r\n// emoji.emoji\r\n// )}\r\n// </Button>\r\n// </TooltipWraper>\r\n// );\r\n// })}\r\n// </div>\r\n","import { forwardRef, useEffect, useState } from \"react\";\r\nimport EmojiList from \"./EmojiList\";\r\nimport { EmojiItem } from \"@tiptap/extension-emoji\";\r\nimport { Editor } from \"@tiptap/core\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\ntype CommandProps = {\r\n name: string;\r\n};\r\n\r\ntype EmojiListProps = {\r\n items: EmojiItem[];\r\n editor: Editor;\r\n command: (arg0: CommandProps) => void;\r\n range: Range;\r\n query: string;\r\n};\r\n\r\nconst EmojiPicker = forwardRef<unknown, EmojiListProps>((props, ref) => {\r\n const t = useMessages();\r\n const { items } = props;\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n\r\n const selectItem = (index: number) => {\r\n const item = props.items[index];\r\n\r\n if (item) {\r\n props.command({ name: item.name });\r\n }\r\n };\r\n\r\n useEffect(() => setSelectedIndex(0), [props.items]);\r\n\r\n return (\r\n <div className=\" p-1.5 scrollbar-none overflow-y-scroll bg-card rounded-xl border shadow-2xl min-h-10\">\r\n {items.length > 0 ? (\r\n <EmojiList\r\n items={items}\r\n selectedIndex={selectedIndex}\r\n onSelectIndex={selectItem}\r\n />\r\n ) : (\r\n <div className=\"p-1 text-sm text-muted-foreground \">{t.emojiPicker.empty}</div>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\nEmojiPicker.displayName = \"EmojiPicker\";\r\nexport default EmojiPicker;\r\n","import { ReactRenderer } from \"@tiptap/react\"\nimport { SuggestionOptions } from \"@tiptap/suggestion\"\nimport ListOfEmoji from \"./EmojiPicker\"\n\nexport default <Omit<SuggestionOptions, 'editor'>>{\n items: ({ editor, query }) => {\n return editor.storage.emoji.emojis.filter(({ shortcodes, tags }) => {\n return (\n shortcodes.find(shortcode => shortcode.startsWith(query.toLowerCase())) ||\n tags.find(tag => tag.startsWith(query.toLowerCase()))\n )\n })\n },\n allow: ({ editor }) => {\n // Node tên htmlCodeBlock không tồn tại nên isActive luôn trả false,\n // tức là menu vẫn bật ngay trong code block.\n const isInsideHTMLCodeBlock = editor.isActive('codeBlock');\n if (isInsideHTMLCodeBlock) {\n return false;\n }\n\n return true;\n },\n render: () => {\n let component: ReactRenderer<any> | null = null;\n let unmountFloating: (() => void) | undefined;\n\n /**\n * Dựng menu và giao việc định vị cho chính plugin.\n *\n * `props.mount` của @tiptap/suggestion lo hết: gắn vào container, giấu\n * cho tới khi có toạ độ thật, autoUpdate neo theo phần tử trang trí bọc\n * ký tự kích hoạt, và đóng khi click ra ngoài. Bản trước tự làm từng\n * bước đó bằng floating-ui, và mỗi bước là một chỗ có thể ném lỗi ngay\n * trong onStart.\n */\n const mount = (props: any) => {\n const renderer = new ReactRenderer(ListOfEmoji, {\n props,\n editor: props.editor,\n });\n unmountFloating = props.mount(renderer.element as HTMLElement);\n return renderer;\n };\n\n const unmount = () => {\n unmountFloating?.();\n unmountFloating = undefined;\n component?.destroy();\n component = null;\n };\n\n return {\n onStart: (props: any) => {\n try {\n component = mount(props);\n } catch (error) {\n // Plugin gọi onStart bên trong một hàm async. Ném ở đây là bỏ\n // luôn phần còn lại của hàm — kể cả bước nạp danh sách — mà\n // không có lỗi nào nổi lên. Bắt lại để nó thật sự thấy được.\n console.error('[notion-editor] không dựng được menu gợi ý:', error);\n unmount();\n }\n },\n\n onUpdate(props: any) {\n if (!component) {\n // onStart hỏng. Plugin dispatch \"updated\" ngay sau \"started\",\n // nên dựng lại ở đây để một lần trục trặc không giết luôn menu\n // cho tới khi người dùng gõ lại từ đầu.\n try {\n component = mount(props);\n } catch (error) {\n console.error('[notion-editor] không dựng lại được menu gợi ý:', error);\n }\n return;\n }\n component.updateProps(props);\n },\n\n onKeyDown(props: any) {\n if (props.event.key === 'Escape') {\n unmount();\n return true;\n }\n return component?.ref?.onKeyDown(props) ?? false;\n },\n\n onExit: unmount,\n };\n }\n}","\"use client\"\r\n\r\nimport * as React from \"react\"\r\nimport { cva, type VariantProps } from \"class-variance-authority\"\r\n\r\nimport { cn } from \"~/lib/utils\"\r\nimport { Button } from \"~/components/ui/button\"\r\nimport { Input } from \"~/components/ui/input\"\r\nimport { Textarea } from \"~/components/ui/textarea\"\r\n\r\nfunction InputGroup({ className, ...props }: React.ComponentProps<\"div\">) {\r\n return (\r\n <div\r\n data-slot=\"input-group\"\r\n role=\"group\"\r\n className={cn(\r\n \"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nconst inputGroupAddonVariants = cva(\r\n \"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4\",\r\n {\r\n variants: {\r\n align: {\r\n \"inline-start\":\r\n \"order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]\",\r\n \"inline-end\":\r\n \"order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]\",\r\n \"block-start\":\r\n \"order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2\",\r\n \"block-end\":\r\n \"order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2\",\r\n },\r\n },\r\n defaultVariants: {\r\n align: \"inline-start\",\r\n },\r\n }\r\n)\r\n\r\nfunction InputGroupAddon({\r\n className,\r\n align = \"inline-start\",\r\n ...props\r\n}: React.ComponentProps<\"div\"> & VariantProps<typeof inputGroupAddonVariants>) {\r\n return (\r\n <div\r\n role=\"group\"\r\n data-slot=\"input-group-addon\"\r\n data-align={align}\r\n className={cn(inputGroupAddonVariants({ align }), className)}\r\n onClick={(e) => {\r\n if ((e.target as HTMLElement).closest(\"button\")) {\r\n return\r\n }\r\n e.currentTarget.parentElement?.querySelector(\"input\")?.focus()\r\n }}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nconst inputGroupButtonVariants = cva(\r\n \"flex items-center gap-2 text-sm shadow-none\",\r\n {\r\n variants: {\r\n size: {\r\n xs: \"h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5\",\r\n sm: \"\",\r\n \"icon-xs\":\r\n \"size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0\",\r\n \"icon-sm\": \"size-8 p-0 has-[>svg]:p-0\",\r\n },\r\n },\r\n defaultVariants: {\r\n size: \"xs\",\r\n },\r\n }\r\n)\r\n\r\nfunction InputGroupButton({\r\n className,\r\n type = \"button\",\r\n variant = \"ghost\",\r\n size = \"xs\",\r\n ...props\r\n}: Omit<React.ComponentProps<typeof Button>, \"size\"> &\r\n VariantProps<typeof inputGroupButtonVariants>) {\r\n return (\r\n <Button\r\n type={type}\r\n data-size={size}\r\n variant={variant}\r\n className={cn(inputGroupButtonVariants({ size }), className)}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction InputGroupText({ className, ...props }: React.ComponentProps<\"span\">) {\r\n return (\r\n <span\r\n className={cn(\r\n \"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction InputGroupInput({\r\n className,\r\n ...props\r\n}: React.ComponentProps<\"input\">) {\r\n return (\r\n <Input\r\n data-slot=\"input-group-control\"\r\n className={cn(\r\n \"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction InputGroupTextarea({\r\n className,\r\n ...props\r\n}: React.ComponentProps<\"textarea\">) {\r\n return (\r\n <Textarea\r\n data-slot=\"input-group-control\"\r\n className={cn(\r\n \"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport {\r\n InputGroup,\r\n InputGroupAddon,\r\n InputGroupButton,\r\n InputGroupText,\r\n InputGroupInput,\r\n InputGroupTextarea,\r\n}\r\n","import * as React from \"react\"\r\nimport { Command as CommandPrimitive } from \"cmdk\"\r\n\r\nimport { cn } from \"~/lib/utils\"\r\nimport {\r\n Dialog,\r\n DialogContent,\r\n DialogDescription,\r\n DialogHeader,\r\n DialogTitle,\r\n} from \"~/components/ui/dialog\"\r\nimport {\r\n InputGroup,\r\n InputGroupAddon,\r\n} from \"~/components/ui/input-group\"\r\nimport { SearchIcon, CheckIcon } from \"lucide-react\"\r\n\r\nfunction Command({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof CommandPrimitive>) {\r\n return (\r\n <CommandPrimitive\r\n data-slot=\"command\"\r\n className={cn(\r\n \"flex size-full flex-col overflow-hidden rounded-xl! bg-popover p-1 text-popover-foreground\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction CommandDialog({\r\n title = \"Command Palette\",\r\n description = \"Search for a command to run...\",\r\n children,\r\n className,\r\n showCloseButton = false,\r\n ...props\r\n}: React.ComponentProps<typeof Dialog> & {\r\n title?: string\r\n description?: string\r\n className?: string\r\n showCloseButton?: boolean\r\n}) {\r\n return (\r\n <Dialog {...props}>\r\n <DialogHeader className=\"sr-only\">\r\n <DialogTitle>{title}</DialogTitle>\r\n <DialogDescription>{description}</DialogDescription>\r\n </DialogHeader>\r\n <DialogContent\r\n className={cn(\r\n \"top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0\",\r\n className\r\n )}\r\n showCloseButton={showCloseButton}\r\n >\r\n {children}\r\n </DialogContent>\r\n </Dialog>\r\n )\r\n}\r\n\r\nfunction CommandInput({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Input>) {\r\n return (\r\n <div data-slot=\"command-input-wrapper\" className=\"p-1 pb-0\">\r\n <InputGroup className=\"h-8! rounded-lg! border-input/30 bg-input/30 shadow-none! *:data-[slot=input-group-addon]:pl-2!\">\r\n <CommandPrimitive.Input\r\n data-slot=\"command-input\"\r\n className={cn(\r\n \"w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n <InputGroupAddon>\r\n <SearchIcon className=\"size-4 shrink-0 opacity-50\" />\r\n </InputGroupAddon>\r\n </InputGroup>\r\n </div>\r\n )\r\n}\r\n\r\nfunction CommandList({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.List>) {\r\n return (\r\n <CommandPrimitive.List\r\n data-slot=\"command-list\"\r\n className={cn(\r\n \"no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction CommandEmpty({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Empty>) {\r\n return (\r\n <CommandPrimitive.Empty\r\n data-slot=\"command-empty\"\r\n className={cn(\"py-6 text-center text-sm\", className)}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction CommandGroup({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Group>) {\r\n return (\r\n <CommandPrimitive.Group\r\n data-slot=\"command-group\"\r\n className={cn(\r\n \"overflow-hidden p-1 text-foreground **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction CommandSeparator({\r\n className,\r\n ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Separator>) {\r\n return (\r\n <CommandPrimitive.Separator\r\n data-slot=\"command-separator\"\r\n className={cn(\"-mx-1 h-px bg-border\", className)}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nfunction CommandItem({\r\n className,\r\n children,\r\n ...props\r\n}: React.ComponentProps<typeof CommandPrimitive.Item>) {\r\n return (\r\n <CommandPrimitive.Item\r\n data-slot=\"command-item\"\r\n className={cn(\r\n \"group/command-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 hover:bg-[var(--tt-menu-item-hover-bg)] data-[selected=true]:bg-[var(--tt-menu-item-hover-bg)] data-[selected=true]:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[selected=true]:*:[svg]:text-foreground\",\r\n className\r\n )}\r\n {...props}\r\n >\r\n {children}\r\n <CheckIcon className=\"ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100\" />\r\n </CommandPrimitive.Item>\r\n )\r\n}\r\n\r\nfunction CommandShortcut({\r\n className,\r\n ...props\r\n}: React.ComponentProps<\"span\">) {\r\n return (\r\n <span\r\n data-slot=\"command-shortcut\"\r\n className={cn(\r\n \"ml-auto text-xs tracking-widest text-muted-foreground group-data-[selected=true]/command-item:text-foreground\",\r\n className\r\n )}\r\n {...props}\r\n />\r\n )\r\n}\r\n\r\nexport {\r\n Command,\r\n CommandDialog,\r\n CommandInput,\r\n CommandList,\r\n CommandEmpty,\r\n CommandGroup,\r\n CommandItem,\r\n CommandShortcut,\r\n CommandSeparator,\r\n}\r\n","import React, {\n useEffect,\n useImperativeHandle,\n useState,\n forwardRef,\n} from \"react\";\nimport type { SuggestionProps } from \"@tiptap/suggestion\";\nimport type { MentionItem } from \"./types\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandList,\n} from \"~/components/ui/command\";\nimport { cn } from \"~/lib/utils\";\nimport { useMessages } from \"~/i18n\";\n\nexport interface DropdownMenuRef {\n onKeyDown: (params: {\n event: KeyboardEvent | React.KeyboardEvent;\n }) => boolean;\n}\n\ntype MentionListProps = SuggestionProps<MentionItem>;\n\nconst MentionList = forwardRef<DropdownMenuRef, MentionListProps>(\n (props, ref) => {\n const t = useMessages();\n const [selectedIndex, setSelectedIndex] = useState<number>(0);\n\n const selectItem = (index: number) => {\n const item = props.items[index];\n if (item) {\n props.command(item);\n }\n };\n\n const upHandler = () => {\n if (props.items.length === 0) return;\n setSelectedIndex(\n (selectedIndex + props.items.length - 1) % props.items.length,\n );\n };\n\n const downHandler = () => {\n if (props.items.length === 0) return;\n setSelectedIndex((selectedIndex + 1) % props.items.length);\n };\n\n const enterHandler = () => {\n selectItem(selectedIndex);\n };\n\n useEffect(() => {\n setSelectedIndex(0);\n }, [props.items]);\n\n useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }) => {\n if (event.key === \"ArrowUp\") {\n upHandler();\n return true;\n }\n\n if (event.key === \"ArrowDown\") {\n downHandler();\n return true;\n }\n\n if (event.key === \"Enter\") {\n enterHandler();\n return true;\n }\n\n return false;\n },\n }));\n\n return (\n <Command className=\"border shadow-2xl w-55 z-[120]\" loop>\n <CommandList>\n {props.loading ? (\n <div className=\"item loading p-2 text-sm text-muted-foreground flex items-center gap-2\">\n <span className=\"loading-dot h-2 w-2 animate-pulse rounded-full bg-foreground\" />\n Loading…\n </div>\n ) : props.items.length > 0 ? (\n <CommandGroup>\n {props.items.map((item, index) => (\n <CommandItem\n key={item.id}\n value={item.label}\n /* Use shadcn/cmdk's standard aria-selected state or fall back to your manual active index classes */\n className={cn(\"rounded-xl\", {\n \"bg-[var(--tt-menu-item-hover-bg)]\": index === selectedIndex,\n })}\n onSelect={() => selectItem(index)}\n >\n {item.label}\n </CommandItem>\n ))}\n </CommandGroup>\n ) : (\n <CommandEmpty>{t.common.noResults}</CommandEmpty>\n )}\n </CommandList>\n </Command>\n );\n },\n);\n\nMentionList.displayName = \"MentionList\";\n\nexport default MentionList;\n","import { ReactRenderer } from \"@tiptap/react\";\nimport type { SuggestionOptions, SuggestionProps } from \"@tiptap/suggestion\";\nimport MentionList, { type DropdownMenuRef } from \"./MentionList\";\nimport type { MentionItem, MentionSource } from \"./types\";\nimport { updatePosition } from \"~/lib/utils\";\n\n/**\n * Dựng cấu hình suggestion cho `@mention` quanh một nguồn dữ liệu do app chủ\n * cung cấp.\n *\n * Bản template gốc hardcode danh sách diễn viên thập niên 80 ngay trong file\n * này. Tách thành factory để cùng một editor phục vụ được nhiều ngữ cảnh\n * (nhân sự, tài liệu, khách hàng…) mà không phải fork extension.\n */\nexport function createMentionSuggestion(\n\tsource: MentionSource,\n): Omit<SuggestionOptions<MentionItem>, \"editor\"> {\n\treturn {\n\t\titems: ({ query }) => source(query),\n\n\t\trender: () => {\n\t\t\tlet component: ReactRenderer<\n\t\t\t\tDropdownMenuRef,\n\t\t\t\tSuggestionProps<MentionItem>\n\t\t\t>;\n\n\t\t\tconst place = (props: SuggestionProps<MentionItem>) => {\n\t\t\t\tif (!props.clientRect) return;\n\t\t\t\tupdatePosition({\n\t\t\t\t\teditor: props.editor,\n\t\t\t\t\telement: component.element as HTMLElement,\n\t\t\t\t\tclientRect: props.clientRect(),\n\t\t\t\t});\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tonStart: (props) => {\n\t\t\t\t\tcomponent = new ReactRenderer(MentionList, {\n\t\t\t\t\t\tprops,\n\t\t\t\t\t\teditor: props.editor,\n\t\t\t\t\t});\n\t\t\t\t\t(component.element as HTMLElement).style.position = \"absolute\";\n\t\t\t\t\tdocument.body.appendChild(component.element);\n\t\t\t\t\tplace(props);\n\t\t\t\t},\n\n\t\t\t\tonUpdate(props) {\n\t\t\t\t\tcomponent.updateProps(props);\n\t\t\t\t\tplace(props);\n\t\t\t\t},\n\n\t\t\t\tonKeyDown(props) {\n\t\t\t\t\tif (props.event.key === \"Escape\") {\n\t\t\t\t\t\tcomponent.destroy();\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn component.ref?.onKeyDown(props) ?? false;\n\t\t\t\t},\n\n\t\t\t\tonExit() {\n\t\t\t\t\tcomponent.element.remove();\n\t\t\t\t\tcomponent.destroy();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n","import Suggestion, { SuggestionOptions } from '@tiptap/suggestion';\r\nimport { Extension } from \"@tiptap/core\";\r\n\r\n\r\nexport type SlashCommandOptions = {\r\n suggestion: Omit<SuggestionOptions, 'editor'>\r\n}\r\n\r\nconst SlashCommand = Extension.create<SlashCommandOptions>({\r\n name: \"slash-command\",\r\n addOptions() {\r\n return {\r\n suggestion: {\r\n char: '/',\r\n command: ({ editor, range, props }) => {\r\n props.command({ editor, range })\r\n },\r\n decorationTag: \"Filters\",\r\n decorationContent: \"Filters\",\r\n decorationClass: 'bg-secondary outline-[5.5px] outline-secondary px-px rounded after:text-muted-foreground',\r\n decorationEmptyClass: \"after:content-[attr(data-decoration-content)]\"\r\n }\r\n }\r\n },\r\n\r\n addProseMirrorPlugins() {\r\n return [\r\n Suggestion({\r\n editor: this.editor,\r\n ...this.options.suggestion\r\n })\r\n ]\r\n }\r\n\r\n})\r\n\r\n\r\n\r\nexport { SlashCommand }","import type { BlockItem } from './types';\r\nimport { Table } from 'lucide-react';\r\n\r\nexport const table: BlockItem = {\r\n title: 'Table',\r\n description: 'Add a call to action button to email.',\r\n searchTerms: ['table', 'row', 'cell'],\r\n icon: <Table />,\r\n command: ({ editor, range }) => {\r\n // @ts-ignore\r\n editor.chain().focus().deleteRange(range).insertTable().run();\r\n },\r\n};\r\n","import type { BlockItem } from './types';\r\nimport { SmilePlus } from 'lucide-react';\r\n\r\nexport const emoji: BlockItem = {\r\n title: 'Emoji',\r\n description: 'Add a emoji to make it look good.',\r\n searchTerms: ['emoji'],\r\n icon: <SmilePlus />,\r\n command: ({ editor, range }) => {\r\n // @ts-ignore\r\n editor.chain().focus().deleteRange(range).insertContent(':').run();\r\n },\r\n};\r\n","import { Minus } from \"lucide-react\";\r\nimport { BlockItem } from \"./types\";\r\n\r\nexport const separator: BlockItem = {\r\n title: \"Separator\",\r\n description: \"Insert a horizontal dividing line.\",\r\n searchTerms: [\"separator\", \"hr\", \"line\", \"divider\"],\r\n icon: <Minus />,\r\n command: ({ editor, range }) => {\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteRange(range) // Removes the slash command text (e.g., \"/separator\")\r\n .setHorizontalRule() // Inserts the Tiptap horizontal rule block\r\n .run();\r\n },\r\n};","import { ListOrdered } from \"lucide-react\";\r\nimport { BlockItem } from \"./types\";\r\n\r\nexport const tableOfContents: BlockItem = {\r\n title: \"Table of Contents\",\r\n description: \"Insert a live outline of your headings.\",\r\n searchTerms: [\"toc\", \"table of contents\", \"outline\", \"headings\"],\r\n icon: <ListOrdered />,\r\n command: ({ editor, range }) => {\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteRange(range) // Removes slash command text (e.g., \"/toc\")\r\n // Node riêng của package; `tableOfContents` là tên Extension chỉ cấp\r\n // storage nên chèn theo tên đó là trỏ vào một node không tồn tại.\r\n .insertContent({ type: \"tableOfContentsNode\" })\r\n .run();\r\n },\r\n};\r\n","import type { BlockItem } from \"./types\";\r\nimport { UploadCloud } from \"lucide-react\";\r\n\r\nexport const uploadImage: BlockItem = {\r\n title: \"Image\",\r\n description: \"Add a to drop zon and upload zone.\",\r\n searchTerms: [\"image\", \"img\", \"upload\", \"drop\"],\r\n icon: <UploadCloud />,\r\n command: ({ editor, range }) => {\r\n // @ts-ignore\r\n editor\r\n .chain()\r\n .focus()\r\n .deleteRange(range)\r\n .insertContent({ type: \"imageUpload\" })\r\n .run();\r\n },\r\n};\r\n","import { Paperclip } from \"lucide-react\";\nimport type { BlockItem } from \"./types\";\n\nexport const attachment: BlockItem = {\n\ttitle: \"Attachment\",\n\tdescription: \"Upload a Word, PDF or spreadsheet file.\",\n\tsearchTerms: [\"file\", \"attachment\", \"word\", \"pdf\", \"excel\", \"doc\", \"tep\", \"tai lieu\"],\n\ticon: <Paperclip />,\n\tcommand: ({ editor, range }) => {\n\t\teditor\n\t\t\t.chain()\n\t\t\t.focus()\n\t\t\t.deleteRange(range)\n\t\t\t.insertContent({ type: \"attachmentUpload\" })\n\t\t\t.run();\n\t},\n};\n","import type { BlockGroupItem, BlockItem } from \"../../blocks/types\";\nimport type { Messages } from \"~/i18n\";\nimport {\n\tblockquote,\n\theading1,\n\theading2,\n\theading3,\n\ttext,\n} from \"../../blocks/typography\";\nimport { bulletList, orderedList, taskList } from \"../../blocks/list\";\nimport { htmlCodeBlock } from \"../../blocks/code\";\nimport { table } from \"../../blocks/table\";\nimport { emoji } from \"../../blocks/emoji\";\nimport { separator } from \"../../blocks/separator\";\nimport { tableOfContents } from \"../../blocks/tableOfContents\";\nimport { uploadImage } from \"~/components/blocks/image\";\nimport { attachment } from \"../../blocks/attachment\";\n\n/**\n * Gắn nhãn đã dịch lên một lệnh.\n *\n * Chuỗi được đặt ở đây thay vì trong từng file block: các file đó định nghĩa\n * *hành vi* của lệnh, còn nhãn thì đổi theo ngôn ngữ. Gom vào một chỗ để thêm\n * ngôn ngữ mới không phải mở mười bảy file.\n */\nconst label = (\n\tblock: BlockItem,\n\t{ title, description }: { title: string; description: string },\n): BlockItem => ({ ...block, title, description });\n\nexport const createBlockItems = (t: Messages): BlockGroupItem[] => [\n\t{\n\t\ttitle: t.slash.groups.style,\n\t\tcommands: [\n\t\t\tlabel(text, t.blocks.text),\n\t\t\tlabel(heading1, t.blocks.heading1),\n\t\t\tlabel(heading2, t.blocks.heading2),\n\t\t\tlabel(heading3, t.blocks.heading3),\n\t\t\tlabel(bulletList, t.blocks.bulletList),\n\t\t\tlabel(orderedList, t.blocks.orderedList),\n\t\t\tlabel(taskList, t.blocks.taskList),\n\t\t\tlabel(blockquote, t.blocks.blockquote),\n\t\t\tlabel(htmlCodeBlock, t.blocks.codeBlock),\n\t\t],\n\t},\n\t{\n\t\ttitle: t.slash.groups.insert,\n\t\tcommands: [\n\t\t\tlabel(emoji, t.blocks.emoji),\n\t\t\tlabel(table, t.blocks.table),\n\t\t\tlabel(separator, t.blocks.separator),\n\t\t\tlabel(tableOfContents, t.blocks.tableOfContents),\n\t\t],\n\t},\n\t{\n\t\ttitle: t.slash.groups.upload,\n\t\tcommands: [\n\t\t\tlabel(uploadImage, t.blocks.image),\n\t\t\tlabel(attachment, t.blocks.attachment),\n\t\t],\n\t},\n];\n","import { BlockGroupItem, BlockItem } from \"../../blocks/types\";\r\n\r\nimport { CommandGroup, CommandItem } from \"~/components/ui/command\";\r\nimport { cn } from \"~/lib/utils\";\r\n\r\ninterface CommandListItemsProps {\r\n groups: BlockGroupItem[];\r\n flatCommands: BlockItem[];\r\n selectedIndex: number;\r\n onSelectIndex: (index: number) => void;\r\n}\r\n\r\nexport const MenuListGroup = ({\r\n groups,\r\n flatCommands,\r\n selectedIndex,\r\n onSelectIndex,\r\n}: CommandListItemsProps) => {\r\n let absoluteIndexOffset = 0;\r\n return (\r\n <div className=\"space-y-2\">\r\n {groups.map((blockGroupItem, i) => {\r\n const currentGroupCommands = blockGroupItem.commands;\r\n const groupStartOffset = absoluteIndexOffset;\r\n absoluteIndexOffset += currentGroupCommands.length;\r\n return (\r\n <CommandGroup\r\n heading={blockGroupItem.title}\r\n key={blockGroupItem.title}\r\n >\r\n {currentGroupCommands.map((block, relativeIndex) => {\r\n const itemIndex = groupStartOffset + relativeIndex;\r\n const isSelected = itemIndex === selectedIndex;\r\n // SlashMenu tự giữ selectedIndex cho phím mũi tên; cmdk không\r\n // biết về nó nên phải tự tô, nếu không điều hướng bằng bàn phím\r\n // sẽ không có phản hồi thị giác nào.\r\n return (\r\n <CommandItem\r\n key={block.title}\r\n onSelect={() => onSelectIndex(itemIndex)}\r\n className={cn(isSelected && \"bg-[var(--tt-menu-item-hover-bg)]\")}\r\n >\r\n {block.icon}\r\n <span>{block.title}</span>\r\n </CommandItem>\r\n );\r\n })}\r\n </CommandGroup>\r\n \r\n );\r\n })}\r\n </div>\r\n );\r\n};\r\n","import { Editor, Range } from \"@tiptap/core\";\r\nimport { BlockGroupItem, BlockItem } from \"../../blocks/types\";\r\nimport { forwardRef, useEffect, useState } from \"react\";\r\nimport { MenuListGroup } from \"./MenuListGroup\";\r\nimport { Command, CommandEmpty, CommandList } from \"~/components/ui/command\";\r\nimport { useMessages } from \"~/i18n\";\r\n\r\ntype CommandListProps = {\r\n items: BlockGroupItem[];\r\n command: (item: BlockItem) => void;\r\n editor: Editor;\r\n range: Range;\r\n query: string;\r\n};\r\n\r\nconst SlashMenu = forwardRef<unknown, CommandListProps>((props, ref) => {\r\n const t = useMessages();\r\n const { items: groups, command, editor, range, query } = props;\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n\r\n const flatCommands = groups.flatMap((group) => group.commands);\r\n\r\n useEffect(() => {\r\n setSelectedIndex(0);\r\n }, [groups]);\r\n\r\n const selectItem = (index: number) => {\r\n const item = flatCommands[index];\r\n if (item) {\r\n command(item);\r\n }\r\n };\r\n\r\n return (\r\n <Command className=\"border shadow-2xl w-55 z-120\">\r\n <CommandList>\r\n <CommandEmpty>{t.common.noResults}</CommandEmpty>\r\n <MenuListGroup\r\n flatCommands={flatCommands}\r\n groups={groups}\r\n selectedIndex={selectedIndex}\r\n onSelectIndex={selectItem}\r\n />\r\n </CommandList>\r\n </Command>\r\n \r\n );\r\n});\r\n\r\nSlashMenu.displayName = \"SlashMenu\";\r\nexport default SlashMenu;\r\n","import { ReactRenderer } from \"@tiptap/react\";\nimport { SuggestionOptions } from \"@tiptap/suggestion\";\n\n\nimport { createBlockItems } from \"./items\";\nimport type { Messages } from \"~/i18n\";\nimport SlashMenu from \"./SlashMenu\";\n\nexport const createSlashSuggestion = (\n t: Messages,\n): Omit<SuggestionOptions, 'editor'> => ({\n // Phần trang trí bọc dấu \"/\": chữ gợi ý đi qua data-decoration-content\n // rồi được CSS đọc bằng attr(), nên nó dịch được mà không cần sửa CSS.\n decorationTag: \"SlashFilter\",\n decorationContent: t.slash.filterHint,\n decorationClass:\n 'bg-secondary outline-[5.5px] outline-secondary px-px rounded after:text-muted-foreground',\n decorationEmptyClass: \"after:content-[attr(data-decoration-content)]\",\n items: ({ query }) => {\n\n const blockItems = createBlockItems(t);\n if (!query) return blockItems;\n const normalizedQuery = query.toLowerCase().trim();\n\n const filtered = blockItems.filter((item) => item.commands.filter((comm) => comm.searchTerms.includes(normalizedQuery)));\n return filtered || [];\n\n\n },\n allow: ({ editor }) => {\n // Node tên htmlCodeBlock không tồn tại nên isActive luôn trả false,\n // tức là menu vẫn bật ngay trong code block.\n const isInsideHTMLCodeBlock = editor.isActive('codeBlock');\n if (isInsideHTMLCodeBlock) {\n return false;\n }\n\n return true;\n },\n render: () => {\n let component: ReactRenderer<any> | null = null;\n let unmountFloating: (() => void) | undefined;\n\n /**\n * Dựng menu và giao việc định vị cho chính plugin.\n *\n * `props.mount` của @tiptap/suggestion lo hết: gắn vào container, giấu\n * cho tới khi có toạ độ thật, autoUpdate neo theo phần tử trang trí bọc\n * ký tự kích hoạt, và đóng khi click ra ngoài. Bản trước tự làm từng\n * bước đó bằng floating-ui, và mỗi bước là một chỗ có thể ném lỗi ngay\n * trong onStart.\n */\n const mount = (props: any) => {\n const renderer = new ReactRenderer(SlashMenu, {\n props,\n editor: props.editor,\n className: 'not-prose',\n });\n unmountFloating = props.mount(renderer.element as HTMLElement);\n return renderer;\n };\n\n const unmount = () => {\n unmountFloating?.();\n unmountFloating = undefined;\n component?.destroy();\n component = null;\n };\n\n return {\n onStart: (props: any) => {\n try {\n component = mount(props);\n } catch (error) {\n // Plugin gọi onStart bên trong một hàm async. Ném ở đây là bỏ\n // luôn phần còn lại của hàm — kể cả bước nạp danh sách — mà\n // không có lỗi nào nổi lên. Bắt lại để nó thật sự thấy được.\n console.error('[notion-editor] không dựng được menu gợi ý:', error);\n unmount();\n }\n },\n\n onUpdate(props: any) {\n if (!component) {\n // onStart hỏng. Plugin dispatch \"updated\" ngay sau \"started\",\n // nên dựng lại ở đây để một lần trục trặc không giết luôn menu\n // cho tới khi người dùng gõ lại từ đầu.\n try {\n component = mount(props);\n } catch (error) {\n console.error('[notion-editor] không dựng lại được menu gợi ý:', error);\n }\n return;\n }\n component.updateProps(props);\n },\n\n onKeyDown(props: any) {\n if (props.event.key === 'Escape') {\n unmount();\n return true;\n }\n return component?.ref?.onKeyDown(props) ?? false;\n },\n\n onExit: unmount,\n };\n }\n});\n","import type { AnyExtension, Editor } from \"@tiptap/core\";\nimport type { Node as ProseMirrorNode } from \"@tiptap/pm/model\";\nimport Blockquote from \"@tiptap/extension-blockquote\";\nimport { Bold } from \"@tiptap/extension-bold\";\nimport { Code } from \"@tiptap/extension-code\";\nimport CodeBlockLowlight from \"@tiptap/extension-code-block-lowlight\";\nimport { Document } from \"@tiptap/extension-document\";\nimport Emoji, { gitHubEmojis } from \"@tiptap/extension-emoji\";\nimport HardBreak from \"@tiptap/extension-hard-break\";\nimport Heading from \"@tiptap/extension-heading\";\nimport { Highlight } from \"@tiptap/extension-highlight\";\nimport HorizontalRule from \"@tiptap/extension-horizontal-rule\";\nimport { Italic } from \"@tiptap/extension-italic\";\nimport { Link } from \"@tiptap/extension-link\";\nimport { ListKit } from \"@tiptap/extension-list\";\nimport { Mathematics } from \"@tiptap/extension-mathematics\";\nimport { Mention } from \"@tiptap/extension-mention\";\nimport Paragraph from \"@tiptap/extension-paragraph\";\nimport { Placeholder } from \"@tiptap/extension-placeholder\";\nimport { Strike } from \"@tiptap/extension-strike\";\nimport { Subscript } from \"@tiptap/extension-subscript\";\nimport { Superscript } from \"@tiptap/extension-superscript\";\nimport { TableOfContents } from \"@tiptap/extension-table-of-contents\";\nimport Text from \"@tiptap/extension-text\";\nimport { TextAlign } from \"@tiptap/extension-text-align\";\nimport { BackgroundColor, Color, TextStyle } from \"@tiptap/extension-text-style\";\nimport { Underline } from \"@tiptap/extension-underline\";\nimport UniqueID from \"@tiptap/extension-unique-id\";\nimport { Dropcursor, Gapcursor, TrailingNode, UndoRedo } from \"@tiptap/extensions\";\nimport { all, createLowlight } from \"lowlight\";\nimport { v4 as uuidv4 } from \"uuid\";\n\nimport type { BlockGroupItem } from \"../blocks/types\";\nimport { DEFAULT_LOCALE, getMessages, type Locale, type Messages } from \"~/i18n\";\nimport { CustomImage } from \"../node/image/CustomImage\";\nimport {\n\tAttachmentNode,\n\tAttachmentUploadNode,\n\ttype AttachmentUploadFunction,\n} from \"../node/attachment\";\nimport { ImageUploadNode } from \"../node/image-upload\";\nimport type { UploadFunction } from \"../node/image-upload/image-upload-node-extension\";\nimport { CustomTableKit } from \"../node/table-node/table-kit\";\nimport { TableOfContentsNode } from \"../node/toc-node/TableOfContentsNode\";\nimport { createFileHandler } from \"./file-handle\";\nimport { GlobalBlockBackground } from \"./global-block-background\";\nimport { Indent } from \"./Indent\";\nimport emojiSuggestion from \"./emoji-picker/emoji-suggestion\";\nimport { createMentionSuggestion } from \"./mention/mentionSuggestion\";\nimport type { MentionSource } from \"./mention/types\";\nimport { SlashCommand } from \"./slash-command/slash-command\";\nimport { createSlashSuggestion } from \"./slash-command/suggestion\";\n\nconst lowlight = createLowlight(all);\n\n/**\n * Chữ mờ chỉ xuất hiện ở hai chỗ: heading rỗng, và document hoàn toàn rỗng.\n *\n * Mặc định của Tiptap là gợi ý ở mọi node rỗng đang có con trỏ, nên mỗi lần\n * xuống dòng giữa bài lại nhấp nháy một dòng chữ mờ — nhiễu chứ không giúp\n * được gì, vì lúc đó người dùng đã biết cách gõ rồi.\n */\nconst buildPlaceholder =\n\t(t: Messages) =>\n\t({ editor, node }: { editor: Editor; node: ProseMirrorNode }) => {\n\t\tif (node.type.name === \"heading\")\n\t\t\treturn t.placeholder.heading(node.attrs.level as number);\n\t\treturn editor.isEmpty ? t.placeholder.emptyDocument : \"\";\n\t};\n\n\nexport type NotionEditorExtensionOptions = {\n\t/** Extension bổ sung của app chủ, nối vào sau bộ mặc định. */\n\textensions?: AnyExtension[];\n\t/** Nhóm block hiển thị trong slash menu. */\n\tblocks?: BlockGroupItem[];\n\t/**\n\t * Chữ mờ cho document rỗng. Heading rỗng luôn hiện `Heading N`, còn các\n\t * node rỗng khác không hiện gì.\n\t *\n\t * Truyền hàm để tự quyết định hoàn toàn — khi đó quy tắc trên bị bỏ qua.\n\t */\n\tplaceholder?:\n\t\t| string\n\t\t| ((props: { editor: Editor; node: ProseMirrorNode }) => string);\n\t/**\n\t * Hàm đẩy ảnh lên server, trả về URL công khai. Không truyền thì khối\n\t * upload ảnh sẽ báo lỗi khi người dùng chọn file, còn kéo-thả rơi về base64.\n\t */\n\tuploadImage?: UploadFunction;\n\t/**\n\t * Hàm đưa tệp (Word, PDF, bảng tính…) lên và trả về URL công khai.\n\t *\n\t * Package chỉ nhận URL — nó không biết gì về R2 hay bất kỳ storage nào.\n\t * Không truyền thì khối đính kèm báo lỗi khi người dùng chọn tệp.\n\t */\n\tuploadAttachment?: AttachmentUploadFunction;\n\t/** Dung lượng tối đa cho tệp đính kèm, tính bằng byte. */\n\tmaxAttachmentSize?: number;\n\t/** Kích thước file tối đa cho khối upload ảnh, tính bằng byte. */\n\tmaxImageSize?: number;\n\t/** Nguồn dữ liệu cho `@mention`. Bỏ trống thì tắt hẳn tính năng mention. */\n\tmentionSource?: MentionSource;\n\t/** Ngôn ngữ của nhãn trong editor. Mặc định tiếng Việt. */\n\tlocale?: Locale;\n};\n\n/**\n * Dựng bộ extension của editor.\n *\n * Mọi thứ phụ thuộc môi trường (upload ảnh, danh bạ mention, placeholder) đều\n * đi vào qua tham số, nên bản thân package không giữ tham chiếu nào tới app\n * chủ.\n */\nexport function extensions(\n\toptions: NotionEditorExtensionOptions = {},\n): AnyExtension[] {\n\tconst {\n\t\textensions: extraExtensions = [],\n\t\tplaceholder,\n\t\tuploadImage,\n\t\tmaxImageSize = 1048576,\n\t\tuploadAttachment,\n\t\tmaxAttachmentSize = 25 * 1024 * 1024,\n\t\tmentionSource,\n\t\tlocale = DEFAULT_LOCALE,\n\t} = options;\n\n\tconst t = getMessages(locale);\n\n\tconst base: AnyExtension[] = [\n\t\tMathematics.configure({\n\t\t\tinlineOptions: {},\n\t\t\tblockOptions: {},\n\t\t\tkatexOptions: {},\n\t\t}),\n\t\t// \"all\" không phải tên node nào, nên cấu hình cũ khiến không node nào\n\t\t// được gán id — và mọi cú nhảy tới heading của mục lục đều trượt.\n\t\tUniqueID.configure({ types: [\"heading\"] }),\n\t\tBlockquote,\n\t\t// Shift+Enter. Bộ extension của template gốc bỏ sót node này trong khi\n\t\t// slash menu vẫn gọi setHardBreak.\n\t\tHardBreak,\n\t\tTrailingNode,\n\t\tCustomImage,\n\t\tAttachmentNode,\n\t\tAttachmentUploadNode.configure({\n\t\t\tlimit: 1,\n\t\t\tmaxSize: maxAttachmentSize,\n\t\t\tupload: uploadAttachment,\n\t\t\tHTMLAttributes: {},\n\t\t}),\n\t\tImageUploadNode.configure({\n\t\t\taccept: \"image/*\",\n\t\t\tlimit: 1,\n\t\t\tmaxSize: maxImageSize,\n\t\t\tupload: uploadImage,\n\t\t\tHTMLAttributes: {},\n\t\t}),\n\t\tDocument,\n\t\tUndoRedo,\n\t\tBold,\n\t\tItalic,\n\t\tStrike,\n\t\tUnderline,\n\t\tCode,\n\t\t// Bộ gốc nạp cả CodeBlock \"trần\" lẫn CodeBlockLowlight ở dưới. Hai\n\t\t// extension cùng đăng ký node \"codeBlock\" nên ProseMirror nhận hai plugin\n\t\t// trùng key và ném RangeError ngay lúc dựng editor view.\n\t\tSubscript,\n\t\tSuperscript,\n\t\tLink.configure({ openOnClick: false, enableClickSelection: true }),\n\t\tTextAlign.configure({\n\t\t\ttypes: [\"heading\", \"paragraph\"],\n\t\t\talignments: [\"left\", \"center\", \"right\", \"justify\"],\n\t\t}),\n\t\tHighlight.configure({ multicolor: true }),\n\t\tTextStyle,\n\t\tColor,\n\t\tBackgroundColor,\n\t\tSlashCommand.configure({ suggestion: createSlashSuggestion(t) }),\n\t\tPlaceholder.configure({\n\t\t\tplaceholder:\n\t\t\t\ttypeof placeholder === \"function\"\n\t\t\t\t\t? placeholder\n\t\t\t\t\t: buildPlaceholder(\n\t\t\t\t\t\t\tplaceholder\n\t\t\t\t\t\t\t\t? { ...t, placeholder: { ...t.placeholder, emptyDocument: placeholder } }\n\t\t\t\t\t\t\t\t: t,\n\t\t\t\t\t\t),\n\t\t\t// Tiptap tự thêm tiền tố `data-`, nên giá trị ở đây phải là\n\t\t\t// \"placeholder\" để DOM nhận `data-placeholder` — đúng thứ CSS đọc bằng\n\t\t\t// attr(). Đặt sẵn \"data-placeholder\" sẽ ra `data-data-placeholder`.\n\t\t\tdataAttribute: \"placeholder\",\n\t\t\t// Chế độ chỉ đọc không nên mời người dùng gõ lệnh.\n\t\t\tshowOnlyWhenEditable: true,\n\t\t}),\n\t\tHeading.configure({ levels: [1, 2, 3] }),\n\t\tParagraph,\n\t\tGlobalBlockBackground,\n\t\tText,\n\t\tListKit.configure({\n\t\t\tlistItem: { HTMLAttributes: { class: \"not-prose\" } },\n\t\t\ttaskList: { HTMLAttributes: { class: \"not-prose\" } },\n\t\t}),\n\t\tTableOfContents.configure({ getId: () => uuidv4() }),\n\t\tTableOfContentsNode,\n\t\tGapcursor,\n\t\tDropcursor.configure({ class: \"transition-all duration-300 ease-out\" }),\n\t\tCustomTableKit,\n\t\tEmoji.configure({\n\t\t\temojis: gitHubEmojis,\n\t\t\tenableEmoticons: true,\n\t\t\tsuggestion: emojiSuggestion,\n\t\t}),\n\t\tHorizontalRule.configure({\n\t\t\tHTMLAttributes: { class: \"my-custom-divider\" },\n\t\t}),\n\t\tIndent.configure({ types: [\"paragraph\", \"heading\"], indentSize: 24 }),\n\t\tCodeBlockLowlight.configure({ lowlight }),\n\t\tcreateFileHandler({ uploadImage, uploadAttachment }),\n\t];\n\n\t// Mention chỉ có nghĩa khi có danh bạ để tra; bật rỗng thì người dùng gõ \"@\"\n\t// và nhận một dropdown trống mãi mãi.\n\tif (mentionSource) {\n\t\tbase.push(\n\t\t\tMention.configure({\n\t\t\t\tHTMLAttributes: { class: \"mention\" },\n\t\t\t\tsuggestion: createMentionSuggestion(mentionSource),\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn [...base, ...extraExtensions];\n}\n","import { useEffect, useImperativeHandle, useMemo, useRef } from \"react\";\nimport type { Editor, JSONContent } from \"@tiptap/core\";\nimport { EditorContent, EditorContext, useEditor } from \"@tiptap/react\";\n\nimport Draggable from \"./components/Draggable\";\nimport ImageBubbleMenu from \"./components/bubble/ImageBubbleMenu\";\nimport MyBubbleMenu from \"./components/bubble/MyBubbleMenu\";\nimport { TableHandle } from \"./components/node/table-node/TableHandle\";\nimport { TocSidebar } from \"./components/toc-sidebar/TocSidebar\";\nimport { TooltipProvider } from \"./components/ui/tooltip\";\nimport {\n\textensions as buildExtensions,\n\ttype NotionEditorExtensionOptions,\n} from \"./components/extensions\";\nimport { cn } from \"./lib/utils\";\nimport { DEFAULT_LOCALE, I18nProvider, type Locale } from \"./i18n\";\nimport {\n\tAttachmentHandlersProvider,\n\ttype AttachmentAttributes,\n} from \"./components/node/attachment\";\n\n// `notion-editor` không có tác dụng tạo kiểu. Nó là móc để app chủ loại trừ\n// editor này khỏi những rule `.tiptap { … }` viết cho editor Tiptap 2 —\n// Tiptap gắn sẵn class `tiptap` nên hai thế hệ editor đụng nhau ở đó.\nconst PROSE_CLASS =\n\t\"notion-editor prose relative dark:prose-invert prose-hr:border-border prose-blockquote:border-l-foreground prose-h1:text-2xl prose-h2:text-xl prose-h3:text-lg prose-pre:bg-popover prose-pre:border prose-pre:border-border prose-pre:text-muted-foreground marker:text-foreground prose-a:text-blue-600 prose-a:hover:text-blue-500 prose-headings:text-inherit focus:outline-none py-10\";\n\nexport type NotionEditorHandle = {\n\t/** Instance Tiptap, cho các thao tác không nằm trong props. */\n\teditor: Editor | null;\n\t/** Nội dung hiện tại dưới dạng JSON. */\n\tgetJSON: () => JSONContent | null;\n\t/** Nội dung hiện tại dưới dạng HTML. */\n\tgetHTML: () => string;\n\t/** Ghi đè toàn bộ nội dung mà không phát `onChange`. */\n\tsetContent: (content: JSONContent | string) => void;\n\tfocus: () => void;\n};\n\nexport type NotionEditorProps = NotionEditorExtensionOptions & {\n\t/**\n\t * Nội dung khởi tạo. Đây là prop **không kiểm soát**: thay đổi giá trị sẽ\n\t * không nạp lại document. Khi chuyển sang document khác, hãy remount bằng\n\t * `key` — cách này rẻ hơn và tránh con trỏ nhảy lung tung khi đang gõ.\n\t */\n\tcontent?: JSONContent | string;\n\t/** Gọi mỗi lần document đổi. */\n\tonChange?: (json: JSONContent, editor: Editor) => void;\n\t/** Gọi một lần khi editor sẵn sàng. */\n\tonReady?: (editor: Editor) => void;\n\t/** false để chuyển sang chế độ chỉ đọc. Đổi lúc chạy được. */\n\teditable?: boolean;\n\t/** Hiện thanh điều hướng mục lục ở lề phải. */\n\tshowToc?: boolean;\n\t/** Kiểu hiển thị mục lục. */\n\ttocVariant?: \"content\" | \"line\";\n\t/**\n\t * Mở một tệp đính kèm không có URL công khai.\n\t *\n\t * Chỉ cần khi `uploadAttachment` trả về `fileId`: lúc đó chỉ app chủ mới\n\t * đổi được id lấy nội dung. Tệp có `src` dùng thẻ <a> thật, không gọi tới.\n\t */\n\tonOpenAttachment?: (attachment: AttachmentAttributes) => void;\n\t/** Xử lý nút \"Replace\" trên bubble menu của ảnh. */\n\tonReplaceImage?: () => void;\n\tclassName?: string;\n\t/** Class gắn vào vùng soạn thảo, nối sau bộ class typography mặc định. */\n\teditorClassName?: string;\n\t/** Ngôn ngữ của nhãn trong editor. Mặc định tiếng Việt. */\n\tlocale?: Locale;\n\tautofocus?: boolean;\n\tref?: React.Ref<NotionEditorHandle>;\n};\n\n/**\n * Editor kiểu Notion: slash command, bubble menu, kéo-thả block, bảng, ảnh,\n * công thức toán và mục lục.\n *\n * Component tự bọc `TooltipProvider` và `EditorContext` nên chỗ dùng chỉ cần\n * render thẻ này. Style phải được import riêng một lần cho cả app:\n * `import \"tiptap-notion-editor/style.css\"`.\n */\nexport function NotionEditor({\n\tcontent,\n\tonChange,\n\tonReady,\n\teditable = true,\n\tshowToc = true,\n\ttocVariant = \"line\",\n\tonOpenAttachment,\n\tonReplaceImage,\n\tclassName,\n\teditorClassName,\n\tlocale = DEFAULT_LOCALE,\n\tautofocus = false,\n\tref,\n\t...extensionOptions\n}: NotionEditorProps) {\n\t// Giữ callback trong ref để việc app chủ truyền hàm inline không phải dựng\n\t// lại toàn bộ schema của editor sau mỗi lần render.\n\tconst onChangeRef = useRef(onChange);\n\tonChangeRef.current = onChange;\n\tconst onReadyRef = useRef(onReady);\n\tonReadyRef.current = onReady;\n\n\tconst {\n\t\textensions: extraExtensions,\n\t\tblocks,\n\t\tplaceholder,\n\t\tuploadImage,\n\t\tmaxImageSize,\n\t\tuploadAttachment,\n\t\tmaxAttachmentSize,\n\t\tmentionSource,\n\t} = extensionOptions;\n\n\tconst editorExtensions = useMemo(\n\t\t() =>\n\t\t\tbuildExtensions({\n\t\t\t\textensions: extraExtensions,\n\t\t\t\tblocks,\n\t\t\t\tplaceholder,\n\t\t\t\tuploadImage,\n\t\t\t\tmaxImageSize,\n\t\t\t\tuploadAttachment,\n\t\t\t\tmaxAttachmentSize,\n\t\t\t\tmentionSource,\n\t\t\t\tlocale,\n\t\t\t}),\n\t\t[extraExtensions, blocks, placeholder, uploadImage, maxImageSize, uploadAttachment, maxAttachmentSize, mentionSource, locale],\n\t);\n\n\t// Phải giữ nguyên identity giữa các lần render. useEditor gọi setOptions\n\t// cho mọi option đổi, và với editorProps thì ProseMirror `setProps` gán lại\n\t// `directPlugins` bằng một mảng mới — điều đó khiến nó huỷ rồi dựng lại\n\t// toàn bộ plugin view. Suggestion nhận `onExit`, menu slash bị gỡ khỏi DOM\n\t// trong khi state của nó vẫn đang active, nên không bao giờ có `onStart`\n\t// thứ hai để dựng lại. Hệ quả: app chủ nào đẩy `onChange` vào state là\n\t// menu tắt ngay ở phím đầu tiên.\n\tconst editorProps = useMemo(\n\t\t() => ({ attributes: { class: cn(PROSE_CLASS, editorClassName) } }),\n\t\t[editorClassName],\n\t);\n\n\tconst editor = useEditor({\n\t\timmediatelyRender: false,\n\t\textensions: editorExtensions,\n\t\tcontent,\n\t\teditable,\n\t\tautofocus,\n\t\teditorProps,\n\t\tonUpdate: ({ editor: instance }) => {\n\t\t\tonChangeRef.current?.(instance.getJSON(), instance);\n\t\t},\n\t\tonCreate: ({ editor: instance }) => {\n\t\t\tonReadyRef.current?.(instance);\n\t\t},\n\t});\n\n\t// `editable` là prop động, nhưng useEditor chỉ đọc nó lúc khởi tạo.\n\tuseEffect(() => {\n\t\tif (editor && editor.isEditable !== editable) {\n\t\t\teditor.setEditable(editable);\n\t\t}\n\t}, [editor, editable]);\n\n\tuseImperativeHandle(\n\t\tref,\n\t\t() => ({\n\t\t\teditor,\n\t\t\tgetJSON: () => editor?.getJSON() ?? null,\n\t\t\tgetHTML: () => editor?.getHTML() ?? \"\",\n\t\t\tsetContent: (next) => editor?.commands.setContent(next, { emitUpdate: false }),\n\t\t\tfocus: () => editor?.commands.focus(),\n\t\t}),\n\t\t[editor],\n\t);\n\n\tif (!editor) return null;\n\n\treturn (\n\t\t<I18nProvider locale={locale}>\n\t\t\t<AttachmentHandlersProvider onOpenAttachment={onOpenAttachment}>\n\t\t\t<TooltipProvider>\n\t\t\t<EditorContext.Provider value={{ editor }}>\n\t\t\t\t<div className={cn(\"notion-like-editor-wrapper\", className)}>\n\t\t\t\t\t<div className=\"notion-like-editor-layout\">\n\t\t\t\t\t\t<EditorContent\n\t\t\t\t\t\t\tclassName=\"notion-like-editor-content\"\n\t\t\t\t\t\t\teditor={editor}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{editable ? (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<Draggable editor={editor} />\n\t\t\t\t\t\t\t\t<TableHandle editor={editor} />\n\t\t\t\t\t\t\t\t<MyBubbleMenu editor={editor} />\n\t\t\t\t\t\t\t\t<ImageBubbleMenu\n\t\t\t\t\t\t\t\t\teditor={editor}\n\t\t\t\t\t\t\t\t\tonReplaceClick={onReplaceImage ?? (() => {})}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{showToc ? <TocSidebar editor={editor} variant={tocVariant} /> : null}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</EditorContext.Provider>\n\t\t\t</TooltipProvider>\n\t\t\t</AttachmentHandlersProvider>\n\t\t</I18nProvider>\n\t);\n}\n\n","import type { JSONContent } from \"@tiptap/core\";\n\nconst FILE_NODES = new Set([\"image\", \"attachment\"]);\n\n/**\n * Thu tất cả URL tệp đang được document tham chiếu.\n *\n * Dùng khi app chủ cần dọn storage sau khi xoá một trang: package biết node nào\n * mang tệp, còn việc xoá thật thì thuộc backend.\n *\n * Bỏ qua `data:` URL — đó là ảnh nhúng base64, không có gì trên storage để xoá.\n */\nexport function collectFileUrls(content: JSONContent | null | undefined): string[] {\n\tconst urls = new Set<string>();\n\n\tconst walk = (node: JSONContent | undefined) => {\n\t\tif (!node) return;\n\t\tif (node.type && FILE_NODES.has(node.type)) {\n\t\t\tconst src = node.attrs?.src;\n\t\t\tif (typeof src === \"string\" && src && !src.startsWith(\"data:\")) {\n\t\t\t\turls.add(src);\n\t\t\t}\n\t\t}\n\t\tnode.content?.forEach(walk);\n\t};\n\n\twalk(content ?? undefined);\n\treturn [...urls];\n}\n"],"names":["cn","inputs","twMerge","clsx","copySelectedNodeToClipboard","editor","selection","doc","targetNode","NodeSelection","TextSelection","schema","domFragment","DOMSerializer","tempDiv","htmlContent","textContent","clipboardData","error","deleteSelectedNode","from","to","depth","duplicateSelectedNode","nodePos","insertPosition","setUniversalBlockBackgroundColor","color","targetNodeName","getSelectionClientRect","left","right","top","bottom","updatePosition","clientRect","element","placement","strategy","middleware","shift","flip","offsetValue","rect","virtualElement","resolvedMiddleware","offset","computePosition","x","y","resolvedStrategy","buttonVariants","cva","Button","className","variant","size","asChild","props","Comp","Slot","jsx","TooltipProvider","delayDuration","TooltipPrimitive","Tooltip","TooltipTrigger","TooltipContent","sideOffset","children","jsxs","TooltipWrapper","content","showTooltip","DropdownMenu","DropdownMenuPrimitive","DropdownMenuPortal","DropdownMenuTrigger","DropdownMenuContent","align","DropdownMenuGroup","DropdownMenuItem","inset","DropdownMenuLabel","DropdownMenuSeparator","DropdownMenuShortcut","DropdownMenuSub","DropdownMenuSubTrigger","ChevronRightIcon","DropdownMenuSubContent","ScrollArea","ScrollAreaPrimitive","ScrollBar","orientation","InsertLeftColumn","InsertRightColumn","InsertRowAbove","InsertRowBelow","TableMergeIcon","TableMenuItem","Icon","label","onClick","disabled","ITEM_CLASSNAME","TextIcon","AlignmentIcon","getTableContext","state","cellPos","$pos","nodeName","$cellPos","tableNode","tablePos","d","map","TableMap","localCellPos","cellRect","insertRowAtBottom","trackHistory","view","tr","firstRow","cells","i","cell","newRow","Fragment","insertPos","deleteEmptyRowFromBottom","lastRowNode","isEmpty","deletePos","moveActiveColumn","direction","dispatch","context","currentColumnIndex","targetColumnIndex","moveTableColumn","moveActiveRow","currentRowIndex","targetRowIndex","moveTableRow","duplicateActiveRow","targetRowNode","duplicatedRow","insertRowOffset","duplicateActiveColumn","relativePositionOffset","r","mapIndex","cellAbsolutePos","originalCellNode","duplicatedCell","insertionPoint","setTableCellStyles","options","backgroundColor","textColor","cellsToUpdate","CellSelection","_node","pos","cellNode","currentAttrs","cellContentStart","cellContentEnd","colorMark","setTableCellAlignment","horizontal","vertical","updatedAttrs","getActiveTableCellAlignment","defaultState","vi","level","limit","maxSizeMb","en","DEFAULT_LOCALE","dictionaries","getMessages","locale","MessagesContext","createContext","I18nProvider","messages","useMemo","useMessages","useContext","AlignmentDropdown","t","TextAlignStart","TextAlignCenter","TextAlignEnd","AlignVerticalJustifyStart","AlignVerticalJustifyCenter","AlignVerticalJustifyEnd","ColorItem","RECENT_COLORS_KEY","MAX_RECENT","colors","createGroupColors","getRecentColors","stored","handleAddRecentUsed","setItems","filtered","c","updatedRecent","ColorsDropdwon","items","useState","recent","PaintBucket","item","getTableColumnMeta","defaultMeta","hasTableDimensionContent","dimension","cellLocalPos","TableMenuPopover","type","pluginKey","opened","setOpened","isRow","TriggerIcon","EllipsisVerticalIcon","EllipsisIcon","triggerStyles","handleOpenChange","op","isInsideTable","isFirstColumn","isLastColumn","isFirstRow","isLastRow","isRowClearable","isColumnClearable","ArrowUp","ArrowLeft","ArrowDown","ArrowRight","SquareX","deleteCellSelection","CopyIcon","Trash","Colors","setColor","DRAG_HANDLE_POSITION","Dragable","isOpen","setIsOpen","currentNodeType","setCurrentNodeType","getNodePosFromHandle","event","dragHandleEl","xCoords","yCoords","handleInsertAtDragPosition","targetPos","resolvedPos","startOfBlock","handleSelectBlockAndOpenMenu","targetDepth","currentNodeName","blockStartPos","node","DragHandle","TooltipWraper","Plus","GripVertical","DropdownContent","handleSetColor","RotateCcw","Copy","Clipboard","Link","Sparkles","ToggleButton","isActive","icon","text","tooltip","renderIcon","React","IconComponent","Separator","decorative","SeparatorPrimitive","AlignLeft","AlignRight","AlignCenter","ImageBubbleMenu","onReplaceClick","alignment","setAlignment","nextAlignment","onReplace","BubbleMenu","Download","RefreshCcw","Trash2","Popover","PopoverPrimitive","PopoverTrigger","PopoverContent","PickLink","url","setUrl","isLink","activeUrl","useEditorState","ctx","useEffect","setLink","useCallback","e","unsetLink","handleKeyDown","CornerDownLeft","ExternalLink","IndentButtons","currentLevel","isMinIndent","isMaxIndent","ListIndentDecrease","ListIndentIncrease","BubbleMoreOption","isSubscript","isSuperscript","isAlignLeft","isAlignRight","isAlignCenter","isAlignJustify","EllipsisVertical","Subscript","Superscript","TextAlignJustify","TextButton","HighlightButton","ColorDropdown","highlightColor","ChevronDown","DropdwonContent","idx","Type","range","heading1","Heading1","heading2","Heading2","heading3","Heading3","blockquote","TextQuote","bulletList","List","orderedList","ListOrdered","taskList","ListTodo","htmlCodeBlock","CodeXmlIcon","buildItems","buildHeadings","TextTurnInto","headings","activeBlock","isParagraph","isBulletList","isOrderedList","isTaskList","isBlockquote","isCodeBlock","headingLevel","activeHeading","h","getTriggerLabel","head","Heading","MyBubbleMenu","isBold","isItalic","isStrikethrough","isUnderline","isCode","Bold","Italic","Underline","Strikethrough","Code","rowMenuPluginKey","PluginKey","columnMenuPluginKey","TableMenuHandlePlugin","menuType","floatingUIOptions","middlewares","ticking","isVisible","currentTablePos","currentCellPos","getMiddlewares","arrow","autoPlacement","hide","inline","selectCells","showMenu","_view","root","hideMenu","handleMouseMove","evt","target","columnResizingPluginKey","tableEditingKey","cellNodePos","tableNodePos","p","tableRoot","table","tableRect","tableCell","tableCellRect","Plugin","value","meta","prevState","prevPluginState","pluginState","pluginStateChanged","selectionChanged","docChanged","composing","findParentNodeClosestToPos","n","TableMenuHandle","ref","pluginProps","rootElementRef","useRef","rootElement","plugin","createPortal","CellMenuPopover","canMergeCell","canSplitCell","canClearContents","a","b","instance","ranges","hasSpannedCell","cellSelectionCount","selectionContentSize","$from","$to","EqualIcon","TableSelectionOverlayPlugin","rects","domRect","selectionRect","findRects","hideOverlay","showOverlay","handleResize","TableSelectionOverlay","addRowPluginKey","AddRowButton","dragMetrics","handleMouseDown","container","tablePosAttr","lastRowElement","handleMouseUp","metrics","deltaY","targetRowChange","TableAddRowPlugin","setVisibility","opacity","insideControls","lastRow","tableWrapper","tableElement","controls","TableAddRowColumnHandle","TableHandle","columnMenuPluginProps","rowMenuPluginProps","addRowPluginProps","tableSelectionOverlayProps","TocUIList","scrollOver","handleScroll","id","TocSidebarContent","TocPopover","Memorized","TocSidebar","activeId","setActiveId","useEvent","handler","handlerRef","useLayoutEffect","args","getAspectRatio","width","height","getNewHeight","aspectRatio","MIN_WIDTH","IMAGE_MAX_WIDTH","IMAGE_MAX_HEIGHT","ImageNodeView","updateAttributes","getPos","src","nodeWidth","nodeHeight","isHovered","setIsHovered","wrapperRef","containerRef","imgRef","maxAllowedWidth","initialXPosition","initialYPosition","currentWidth","currentHeight","newWidth","newHeight","transformX","transformY","removeListeners","mouseMoveHandler","finalWidth","NodeViewWrapper","CustomImage","Image","val","attributes","ReactNodeViewRenderer","AttachmentHandlersContext","AttachmentHandlersProvider","onOpenAttachment","useAttachmentHandlers","iconFor","mime","name","probe","FileText","FileSpreadsheet","Presentation","FileArchive","FileIcon","DOWNLOAD_CLASS","formatSize","bytes","units","unit","AttachmentNodeView","selected","attrs","AttachmentNode","Node","el","raw","HTMLAttributes","rest","mergeAttributes","commands","isValidPosition","AttachmentUploadNodeView","inputRef","uploading","setUploading","maxSize","handleFile","file","uploaded","Paperclip","AttachmentUploadNode","MAX_FILE_SIZE","focusNextNode","nextSel","Selection","paragraphType","end","para","$inside","handleImageUpload","onProgress","abortSignal","progress","resolve","DropZoneContent","FileImage","CloudUploadIcon","ImageUploadDragArea","onFile","isDragOver","setIsDragOver","isDragActive","setIsDragActive","handleDragEnter","handleDragLeave","handleDragOver","handleDrop","files","ImageUploadPreview","fileItem","onRemove","formatFileSize","k","sizes","XIcon","useFileUpload","fileItems","setFileItems","uploadFile","abortController","fileId","newFileItem","prev","uploadPromises","fileToRemove","ImageUploadNode","accept","extension","uploadOptions","uploadFiles","removeFileItem","clearAllFiles","handleUpload","urls","imageNodes","index","filename","handleChange","handleClick","hasFiles","ImageUploadNodeComponent","nodeAfter","nodeEl","firstChild","CustomTable","Table","colgroup","tableWidth","tableMinWidth","createColGroup","dom","wrapper","tableContainer","key","colGroup","tableControls","tableSelectionContainer","_mutation","CustomTableKit","Extension","TableHeader","TableRow","TableCell","TocNodeView","TableOfContentsNode","isImage","createFileHandler","uploadImage","uploadAttachment","insert","image","upload","reader","FileHandler","GlobalBlockBackground","Indent","setIndentLevel","levelUpdate","isChanged","currentIndent","newIndent","empty","$anchor","EmojiList","onSelectIndex","parentRef","COLUMNS","BUTTON_SIZE","GAP","rows","result","rowVirtualizer","useVirtualizer","virtualRow","rowItems","emoji","colIdx","originalIndex","EmojiPicker","forwardRef","selectedIndex","setSelectedIndex","selectItem","emojiSuggestion","query","shortcodes","tags","shortcode","tag","component","unmountFloating","mount","renderer","ReactRenderer","ListOfEmoji","unmount","Command","CommandPrimitive","CommandList","CommandEmpty","CommandGroup","CommandItem","CheckIcon","MentionList","upHandler","downHandler","enterHandler","useImperativeHandle","createMentionSuggestion","source","place","SlashCommand","Suggestion","SmilePlus","separator","Minus","tableOfContents","UploadCloud","attachment","block","title","description","createBlockItems","MenuListGroup","groups","flatCommands","absoluteIndexOffset","blockGroupItem","currentGroupCommands","groupStartOffset","relativeIndex","itemIndex","isSelected","SlashMenu","command","group","createSlashSuggestion","blockItems","normalizedQuery","comm","lowlight","createLowlight","all","buildPlaceholder","extensions","extraExtensions","placeholder","maxImageSize","maxAttachmentSize","mentionSource","base","Mathematics","UniqueID","Blockquote","HardBreak","TrailingNode","Document","UndoRedo","Strike","TextAlign","Highlight","TextStyle","Color","BackgroundColor","Placeholder","Paragraph","Text","ListKit","TableOfContents","uuidv4","Gapcursor","Dropcursor","Emoji","gitHubEmojis","HorizontalRule","CodeBlockLowlight","Mention","PROSE_CLASS","NotionEditor","onChange","onReady","editable","showToc","tocVariant","onReplaceImage","editorClassName","autofocus","extensionOptions","onChangeRef","onReadyRef","blocks","editorExtensions","buildExtensions","editorProps","useEditor","next","EditorContext","EditorContent","Draggable","FILE_NODES","collectFileUrls","walk"],"mappings":"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"}
|