diff --git a/packages/editor/src/ce/extensions/document-extensions.tsx b/packages/editor/src/ce/extensions/document-extensions.tsx index 2809fcee4..e3c94fa0e 100644 --- a/packages/editor/src/ce/extensions/document-extensions.tsx +++ b/packages/editor/src/ce/extensions/document-extensions.tsx @@ -14,7 +14,8 @@ type Props = { }; export const DocumentEditorAdditionalExtensions = (_props: Props) => { - const extensions: Extensions = [SlashCommands()]; + const { disabledExtensions } = _props; + const extensions: Extensions = disabledExtensions?.includes("slash-commands") ? [] : [SlashCommands()]; return extensions; }; diff --git a/packages/editor/src/core/components/editors/document/collaborative-editor.tsx b/packages/editor/src/core/components/editors/document/collaborative-editor.tsx index a008d5c60..cd7d6f354 100644 --- a/packages/editor/src/core/components/editors/document/collaborative-editor.tsx +++ b/packages/editor/src/core/components/editors/document/collaborative-editor.tsx @@ -14,6 +14,7 @@ import { EditorRefApi, ICollaborativeDocumentEditor } from "@/types"; const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => { const { + onTransaction, aiHandler, containerClassName, disabledExtensions, @@ -43,6 +44,7 @@ const CollaborativeDocumentEditor = (props: ICollaborativeDocumentEditor) => { // use document editor const { editor, hasServerConnectionFailed, hasServerSynced } = useCollaborativeEditor({ + onTransaction, disabledExtensions, editorClassName, embedHandler, diff --git a/packages/editor/src/core/components/editors/editor-wrapper.tsx b/packages/editor/src/core/components/editors/editor-wrapper.tsx index 3e00dc2af..33f011535 100644 --- a/packages/editor/src/core/components/editors/editor-wrapper.tsx +++ b/packages/editor/src/core/components/editors/editor-wrapper.tsx @@ -28,6 +28,9 @@ export const EditorWrapper: React.FC = (props) => { forwardedRef, mentionHandler, onChange, + onTransaction, + handleEditorReady, + autofocus, placeholder, tabIndex, value, @@ -43,6 +46,9 @@ export const EditorWrapper: React.FC = (props) => { initialValue, mentionHandler, onChange, + onTransaction, + handleEditorReady, + autofocus, placeholder, tabIndex, value, diff --git a/packages/editor/src/core/components/editors/lite-text/editor.tsx b/packages/editor/src/core/components/editors/lite-text/editor.tsx index 924706aae..849a3c3e2 100644 --- a/packages/editor/src/core/components/editors/lite-text/editor.tsx +++ b/packages/editor/src/core/components/editors/lite-text/editor.tsx @@ -1,4 +1,4 @@ -import { forwardRef } from "react"; +import { forwardRef, useMemo } from "react"; // components import { EditorWrapper } from "@/components/editors/editor-wrapper"; // extensions @@ -7,9 +7,15 @@ import { EnterKeyExtension } from "@/extensions"; import { EditorRefApi, ILiteTextEditor } from "@/types"; const LiteTextEditor = (props: ILiteTextEditor) => { - const { onEnterKeyPress } = props; + const { onEnterKeyPress, disabledExtensions, extensions: externalExtensions = [] } = props; - const extensions = [EnterKeyExtension(onEnterKeyPress)]; + const extensions = useMemo( + () => [ + ...externalExtensions, + ...(disabledExtensions?.includes("enter-key") ? [] : [EnterKeyExtension(onEnterKeyPress)]), + ], + [externalExtensions, disabledExtensions, onEnterKeyPress] + ); return ; }; diff --git a/packages/editor/src/core/components/editors/rich-text/editor.tsx b/packages/editor/src/core/components/editors/rich-text/editor.tsx index 53f766ee2..87dba8b4d 100644 --- a/packages/editor/src/core/components/editors/rich-text/editor.tsx +++ b/packages/editor/src/core/components/editors/rich-text/editor.tsx @@ -8,24 +8,31 @@ import { SideMenuExtension, SlashCommands } from "@/extensions"; import { EditorRefApi, IRichTextEditor } from "@/types"; const RichTextEditor = (props: IRichTextEditor) => { - const { dragDropEnabled } = props; + const { + disabledExtensions, + dragDropEnabled, + bubbleMenuEnabled = true, + extensions: externalExtensions = [], + } = props; const getExtensions = useCallback(() => { - const extensions = [SlashCommands()]; - - extensions.push( + const extensions = [ + ...externalExtensions, SideMenuExtension({ aiEnabled: false, dragDropEnabled: !!dragDropEnabled, - }) - ); + }), + ]; + if (!disabledExtensions?.includes("slash-commands")) { + extensions.push(SlashCommands()); + } return extensions; - }, [dragDropEnabled]); + }, [dragDropEnabled, disabledExtensions, externalExtensions]); return ( - {(editor) => <>{editor && }} + {(editor) => <>{editor && bubbleMenuEnabled && }} ); }; diff --git a/packages/editor/src/core/components/menus/menu-items.ts b/packages/editor/src/core/components/menus/menu-items.ts index 27f2124d2..0ece455ed 100644 --- a/packages/editor/src/core/components/menus/menu-items.ts +++ b/packages/editor/src/core/components/menus/menu-items.ts @@ -20,10 +20,12 @@ import { Heading6, CaseSensitive, LucideIcon, + MinusSquare, Palette, } from "lucide-react"; // helpers import { + insertHorizontalRule, insertImage, insertTableCommand, setText, @@ -208,6 +210,15 @@ export const ImageItem = (editor: Editor) => icon: ImageIcon, }) as const; +export const HorizontalRuleItem = (editor: Editor) => + ({ + key: "divider", + name: "Divider", + isActive: () => editor?.isActive("horizontalRule"), + command: () => insertHorizontalRule(editor), + icon: MinusSquare, + }) as const; + export const TextColorItem = (editor: Editor): EditorMenuItem => ({ key: "text-color", name: "Color", @@ -246,6 +257,7 @@ export const getEditorMenuItems = (editor: Editor | null): EditorMenuItem[] => { QuoteItem(editor), TableItem(editor), ImageItem(editor), + HorizontalRuleItem(editor), TextColorItem(editor), BackgroundColorItem(editor), ]; diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 65d9a3843..f1a85ab1b 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -1,7 +1,7 @@ import React, { useRef, useState, useCallback, useLayoutEffect, useEffect } from "react"; import { NodeSelection } from "@tiptap/pm/state"; // extensions -import { CustomImageNodeViewProps, ImageToolbarRoot } from "@/extensions/custom-image"; +import { CustoBaseImageNodeViewProps, ImageToolbarRoot } from "@/extensions/custom-image"; // helpers import { cn } from "@/helpers/common"; @@ -37,7 +37,7 @@ const ensurePixelString = (value: Pixel | TDefault | number | undefin return value; }; -type CustomImageBlockProps = CustomImageNodeViewProps & { +type CustomImageBlockProps = CustoBaseImageNodeViewProps & { imageFromFileSystem: string; setFailedToLoadImage: (isError: boolean) => void; editorContainer: HTMLDivElement | null; @@ -56,10 +56,10 @@ export const CustomImageBlock: React.FC = (props) => { getPos, editor, editorContainer, - src: remoteImageSrc, + src: resolvedImageSrc, setEditorContainer, } = props; - const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs; + const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio, src: imgNodeSrc } = node.attrs; // states const [size, setSize] = useState({ width: ensurePixelString(nodeWidth, "35%"), @@ -210,13 +210,13 @@ export const CustomImageBlock: React.FC = (props) => { // show the image loader if the remote image's src or preview image from filesystem is not set yet (while loading the image post upload) (or) // if the initial resize (from 35% width and "auto" height attrs to the actual size in px) is not complete - const showImageLoader = !(remoteImageSrc || imageFromFileSystem) || !initialResizeComplete || hasErroredOnFirstLoad; + const showImageLoader = !(resolvedImageSrc || imageFromFileSystem) || !initialResizeComplete || hasErroredOnFirstLoad; // show the image utils only if the remote image's (post upload) src is set and the initial resize is complete (but not while we're showing the preview imageFromFileSystem) - const showImageUtils = remoteImageSrc && initialResizeComplete; + const showImageUtils = resolvedImageSrc && initialResizeComplete; // show the image resizer only if the editor is editable, the remote image's (post upload) src is set and the initial resize is complete (but not while we're showing the preview imageFromFileSystem) - const showImageResizer = editor.isEditable && remoteImageSrc && initialResizeComplete; + const showImageResizer = editor.isEditable && resolvedImageSrc && initialResizeComplete; // show the preview image from the file system if the remote image's src is not set - const displayedImageSrc = remoteImageSrc || imageFromFileSystem; + const displayedImageSrc = resolvedImageSrc || imageFromFileSystem; return (
= (props) => { try { setHasErroredOnFirstLoad(true); // this is a type error from tiptap, don't remove await until it's fixed - await editor?.commands.restoreImage?.(node.attrs.src); - imageRef.current.src = remoteImageSrc; + await editor?.commands.restoreImage?.(imgNodeSrc); + imageRef.current.src = resolvedImageSrc; } catch { // if the image failed to even restore, then show the error state setFailedToLoadImage(true); @@ -264,7 +264,7 @@ export const CustomImageBlock: React.FC = (props) => { // hide the image while the background calculations of the image loader are in progress (to avoid flickering) and show the loader until then hidden: showImageLoader, "read-only-image": !editor.isEditable, - "blur-sm opacity-80 loading-image": !remoteImageSrc, + "blur-sm opacity-80 loading-image": !resolvedImageSrc, })} style={{ width: size.width, @@ -277,14 +277,14 @@ export const CustomImageBlock: React.FC = (props) => { "absolute top-1 right-1 z-20 bg-black/40 rounded opacity-0 pointer-events-none group-hover/image-component:opacity-100 group-hover/image-component:pointer-events-auto transition-opacity" } image={{ - src: remoteImageSrc, + src: resolvedImageSrc, aspectRatio: size.aspectRatio, height: size.height, width: size.width, }} /> )} - {selected && displayedImageSrc === remoteImageSrc && ( + {selected && displayedImageSrc === resolvedImageSrc && (
)} {showImageResizer && ( diff --git a/packages/editor/src/core/extensions/custom-image/components/image-node.tsx b/packages/editor/src/core/extensions/custom-image/components/image-node.tsx index bdb8280c5..78caa87b3 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-node.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-node.tsx @@ -3,23 +3,24 @@ import { Editor, NodeViewProps, NodeViewWrapper } from "@tiptap/react"; // extensions import { CustomImageBlock, CustomImageUploader, ImageAttributes } from "@/extensions/custom-image"; -export type CustomImageComponentProps = { +export type CustoBaseImageNodeViewProps = { getPos: () => number; editor: Editor; node: NodeViewProps["node"] & { attrs: ImageAttributes; }; - updateAttributes: (attrs: ImageAttributes) => void; + updateAttributes: (attrs: Partial) => void; selected: boolean; }; -export type CustomImageNodeViewProps = NodeViewProps & CustomImageComponentProps; +export type CustomImageNodeProps = NodeViewProps & CustoBaseImageNodeViewProps; -export const CustomImageNode = (props: CustomImageNodeViewProps) => { +export const CustomImageNode = (props: CustomImageNodeProps) => { const { getPos, editor, node, updateAttributes, selected } = props; - const { src: remoteImageSrc } = node.attrs; + const { src: imgNodeSrc } = node.attrs; const [isUploaded, setIsUploaded] = useState(false); + const [resolvedSrc, setResolvedSrc] = useState(undefined); const [imageFromFileSystem, setImageFromFileSystem] = useState(undefined); const [failedToLoadImage, setFailedToLoadImage] = useState(false); @@ -39,13 +40,22 @@ export const CustomImageNode = (props: CustomImageNodeViewProps) => { // the image is already uploaded if the image-component node has src attribute // and we need to remove the blob from our file system useEffect(() => { - if (remoteImageSrc) { + if (resolvedSrc) { setIsUploaded(true); setImageFromFileSystem(undefined); } else { setIsUploaded(false); } - }, [remoteImageSrc]); + }, [resolvedSrc]); + + useEffect(() => { + const getImageSource = async () => { + // @ts-expect-error function not expected here, but will still work and don't remove await + const url: string = await editor?.commands?.getImageSource?.(imgNodeSrc); + setResolvedSrc(url as string); + }; + getImageSource(); + }, [imageFromFileSystem, node.attrs.src]); return ( @@ -55,8 +65,7 @@ export const CustomImageNode = (props: CustomImageNodeViewProps) => { imageFromFileSystem={imageFromFileSystem} editorContainer={editorContainer} editor={editor} - // @ts-expect-error function not expected here, but will still work - src={editor?.commands?.getImageSource?.(remoteImageSrc)} + src={resolvedSrc} getPos={getPos} node={node} setEditorContainer={setEditorContainer} diff --git a/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx b/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx index 36f1361ee..8ad99bc44 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx @@ -5,9 +5,9 @@ import { cn } from "@/helpers/common"; // hooks import { useUploader, useDropZone, uploadFirstImageAndInsertRemaining } from "@/hooks/use-file-upload"; // extensions -import { type CustomImageComponentProps, getImageComponentImageFileMap } from "@/extensions/custom-image"; +import { CustoBaseImageNodeViewProps, getImageComponentImageFileMap } from "@/extensions/custom-image"; -type CustomImageUploaderProps = CustomImageComponentProps & { +type CustomImageUploaderProps = CustoBaseImageNodeViewProps & { maxFileSize: number; loadImageFromFileSystem: (file: string) => void; failedToLoadImage: boolean; diff --git a/packages/editor/src/core/extensions/custom-image/custom-image.ts b/packages/editor/src/core/extensions/custom-image/custom-image.ts index effc5ee16..a232bb258 100644 --- a/packages/editor/src/core/extensions/custom-image/custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/custom-image.ts @@ -24,8 +24,8 @@ declare module "@tiptap/core" { imageComponent: { insertImageComponent: ({ file, pos, event }: InsertImageComponentProps) => ReturnType; uploadImage: (file: File) => () => Promise | undefined; + getImageSource?: (path: string) => () => Promise; restoreImage: (src: string) => () => Promise; - getImageSource?: (path: string) => () => string; }; } } @@ -193,10 +193,10 @@ export const CustomImageExtension = (props: TFileHandler) => { const fileUrl = await upload(file); return fileUrl; }, + getImageSource: (path: string) => async () => await getAssetSrc(path), restoreImage: (src: string) => async () => { await restoreImageFn(src); }, - getImageSource: (path: string) => () => getAssetSrc(path), }; }, diff --git a/packages/editor/src/core/extensions/custom-image/read-only-custom-image.ts b/packages/editor/src/core/extensions/custom-image/read-only-custom-image.ts index 07d890cd6..3248329f0 100644 --- a/packages/editor/src/core/extensions/custom-image/read-only-custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/read-only-custom-image.ts @@ -68,7 +68,7 @@ export const CustomReadOnlyImageExtension = (props: Pick () => getAssetSrc(path), + getImageSource: (path: string) => async () => await getAssetSrc(path), }; }, diff --git a/packages/editor/src/core/extensions/image/extension.tsx b/packages/editor/src/core/extensions/image/extension.tsx index f7666bfe2..f549719f2 100644 --- a/packages/editor/src/core/extensions/image/extension.tsx +++ b/packages/editor/src/core/extensions/image/extension.tsx @@ -76,7 +76,7 @@ export const ImageExtension = (fileHandler: TFileHandler) => { addCommands() { return { - getImageSource: (path: string) => () => getAssetSrc(path), + getImageSource: (path: string) => async () => await getAssetSrc(path), }; }, diff --git a/packages/editor/src/core/extensions/image/read-only-image.tsx b/packages/editor/src/core/extensions/image/read-only-image.tsx index c884a43ee..ce1581a8e 100644 --- a/packages/editor/src/core/extensions/image/read-only-image.tsx +++ b/packages/editor/src/core/extensions/image/read-only-image.tsx @@ -26,7 +26,7 @@ export const ReadOnlyImageExtension = (props: Pick) addCommands() { return { - getImageSource: (path: string) => () => getAssetSrc(path), + getImageSource: (path: string) => async () => await getAssetSrc(path), }; }, diff --git a/packages/editor/src/core/extensions/mentions/mention-node-view.tsx b/packages/editor/src/core/extensions/mentions/mention-node-view.tsx index ca2f39b8c..5121eae4a 100644 --- a/packages/editor/src/core/extensions/mentions/mention-node-view.tsx +++ b/packages/editor/src/core/extensions/mentions/mention-node-view.tsx @@ -18,16 +18,22 @@ export const MentionNodeView = (props) => { useEffect(() => { if (!props.extension.options.mentionHighlights) return; const hightlights = async () => { - const userId = await props.extension.options.mentionHighlights(); + const userId = await props.extension.options.mentionHighlights?.(); setHighlightsState(userId); }; hightlights(); }, [props.extension.options]); + const handleClick = (event: React.MouseEvent) => { + if (!props.node.attrs.redirect_uri) { + event.preventDefault(); + } + }; + return ( { + if (range) editor.chain().focus().deleteRange(range).setHorizontalRule().run(); + else editor.chain().focus().setHorizontalRule().run(); +} export const insertCallout = (editor: Editor, range?: Range) => { if (range) editor.chain().focus().deleteRange(range).insertCallout().run(); else editor.chain().focus().insertCallout().run(); diff --git a/packages/editor/src/core/helpers/scroll-to-node.ts b/packages/editor/src/core/helpers/scroll-to-node.ts index 65d32a7d2..973f3cf14 100644 --- a/packages/editor/src/core/helpers/scroll-to-node.ts +++ b/packages/editor/src/core/helpers/scroll-to-node.ts @@ -32,6 +32,26 @@ function scrollToNode(editor: Editor, pos: number): void { } } +export function scrollToNodeViaDOMCoordinates(editor: Editor, pos: number, behavior?: ScrollBehavior): void { + const view = editor.view; + + // Get the coordinates of the position + const coords = view.coordsAtPos(pos); + + if (coords) { + // Scroll to the coordinates + window.scrollTo({ + top: coords.top + window.scrollY - window.innerHeight / 2, + behavior: behavior, + }); + + // Optionally, you can also focus the editor + view.focus(); + } else { + console.warn("Unable to find coordinates for the given position"); + } +} + export function scrollSummary(editor: Editor, marking: IMarking) { if (editor) { const pos = findNthH1(editor, marking.sequence, marking.level); diff --git a/packages/editor/src/core/hooks/use-collaborative-editor.ts b/packages/editor/src/core/hooks/use-collaborative-editor.ts index ea3c4877e..798581b37 100644 --- a/packages/editor/src/core/hooks/use-collaborative-editor.ts +++ b/packages/editor/src/core/hooks/use-collaborative-editor.ts @@ -13,6 +13,7 @@ import { TCollaborativeEditorProps } from "@/types"; export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => { const { + onTransaction, disabledExtensions, editorClassName, editorProps = {}, @@ -75,6 +76,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => { const editor = useEditor({ id, + onTransaction, editorProps, editorClassName, enableHistory: false, diff --git a/packages/editor/src/core/hooks/use-editor.ts b/packages/editor/src/core/hooks/use-editor.ts index beee9c929..67d779966 100644 --- a/packages/editor/src/core/hooks/use-editor.ts +++ b/packages/editor/src/core/hooks/use-editor.ts @@ -12,7 +12,7 @@ import { CoreEditorExtensions } from "@/extensions"; // helpers import { getParagraphCount } from "@/helpers/common"; import { insertContentAtSavedSelection } from "@/helpers/insert-content-at-cursor-position"; -import { IMarking, scrollSummary } from "@/helpers/scroll-to-node"; +import { IMarking, scrollSummary, scrollToNodeViaDOMCoordinates } from "@/helpers/scroll-to-node"; // props import { CoreEditorProps } from "@/props"; // types @@ -33,6 +33,8 @@ export interface CustomEditorProps { suggestions?: () => Promise; }; onChange?: (json: object, html: string) => void; + onTransaction?: () => void; + autofocus?: boolean; placeholder?: string | ((isFocused: boolean, value: string) => string); provider?: HocuspocusProvider; tabIndex?: number; @@ -54,10 +56,12 @@ export const useEditor = (props: CustomEditorProps) => { initialValue, mentionHandler, onChange, + onTransaction, placeholder, provider, tabIndex, value, + autofocus = false, } = props; // states @@ -66,6 +70,7 @@ export const useEditor = (props: CustomEditorProps) => { const editorRef: MutableRefObject = useRef(null); const savedSelectionRef = useRef(savedSelection); const editor = useTiptapEditor({ + autofocus, editorProps: { ...CoreEditorProps({ editorClassName, @@ -87,7 +92,10 @@ export const useEditor = (props: CustomEditorProps) => { ], content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "

", onCreate: () => handleEditorReady?.(true), - onTransaction: ({ editor }) => setSavedSelection(editor.state.selection), + onTransaction: ({ editor }) => { + setSavedSelection(editor.state.selection); + onTransaction?.(); + }, onUpdate: ({ editor }) => onChange?.(editor.getJSON(), editor.getHTML()), onDestroy: () => handleEditorReady?.(false), }); @@ -120,6 +128,13 @@ export const useEditor = (props: CustomEditorProps) => { useImperativeHandle( forwardedRef, () => ({ + blur: () => editorRef.current?.commands.blur(), + scrollToNodeViaDOMCoordinates(behavior?: ScrollBehavior, pos?: number) { + const resolvedPos = pos ?? savedSelection?.from; + if (!editorRef.current || !resolvedPos) return; + scrollToNodeViaDOMCoordinates(editorRef.current, resolvedPos, behavior); + }, + getCurrentCursorPosition: () => savedSelection?.from, clearEditor: (emitUpdate = false) => { editorRef.current?.chain().setMeta("skipImageDeletion", true).clearContent(emitUpdate).run(); }, diff --git a/packages/editor/src/core/types/collaboration.ts b/packages/editor/src/core/types/collaboration.ts index 60721a5a6..8609995ed 100644 --- a/packages/editor/src/core/types/collaboration.ts +++ b/packages/editor/src/core/types/collaboration.ts @@ -36,6 +36,7 @@ type TCollaborativeEditorHookProps = { }; export type TCollaborativeEditorProps = TCollaborativeEditorHookProps & { + onTransaction?: () => void; embedHandler?: TEmbedConfig; fileHandler: TFileHandler; forwardedRef?: React.MutableRefObject; diff --git a/packages/editor/src/core/types/config.ts b/packages/editor/src/core/types/config.ts index 67043ef9a..3bb4d1af2 100644 --- a/packages/editor/src/core/types/config.ts +++ b/packages/editor/src/core/types/config.ts @@ -1,7 +1,7 @@ import { DeleteImage, RestoreImage, UploadImage } from "@/types"; export type TFileHandler = { - getAssetSrc: (path: string) => string; + getAssetSrc: (path: string) => Promise; cancel: () => void; delete: DeleteImage; upload: UploadImage; diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index bdaf70d68..97f22e772 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -35,6 +35,9 @@ export type EditorReadOnlyRefApi = { }; export interface EditorRefApi extends EditorReadOnlyRefApi { + blur: () => void; + scrollToNodeViaDOMCoordinates: (behavior?: ScrollBehavior, position?: number) => void; + getCurrentCursorPosition: () => number | undefined; setEditorValueAtCursorPosition: (content: string) => void; executeMenuItemCommand: ( props: @@ -68,6 +71,7 @@ export interface EditorRefApi extends EditorReadOnlyRefApi { export interface IEditorProps { containerClassName?: string; displayConfig?: TDisplayConfig; + disabledExtensions?: TExtensions[]; editorClassName?: string; fileHandler: TFileHandler; forwardedRef?: React.MutableRefObject; @@ -78,22 +82,26 @@ export interface IEditorProps { suggestions?: () => Promise; }; onChange?: (json: object, html: string) => void; + onTransaction?: () => void; + handleEditorReady?: (value: boolean) => void; + autofocus?: boolean; onEnterKeyPress?: (e?: any) => void; placeholder?: string | ((isFocused: boolean, value: string) => string); tabIndex?: number; - value?: string | null; + value?: string | null; +} +export interface ILiteTextEditor extends IEditorProps { + extensions?: any[]; } - -export type ILiteTextEditor = IEditorProps; - export interface IRichTextEditor extends IEditorProps { + extensions?: any[]; + bubbleMenuEnabled?: boolean; dragDropEnabled?: boolean; } export interface ICollaborativeDocumentEditor extends Omit { aiHandler?: TAIHandler; - disabledExtensions: TExtensions[]; embedHandler: TEmbedConfig; handleEditorReady?: (value: boolean) => void; id: string; diff --git a/packages/editor/src/core/types/extensions.ts b/packages/editor/src/core/types/extensions.ts index da8713f10..2be17a4ef 100644 --- a/packages/editor/src/core/types/extensions.ts +++ b/packages/editor/src/core/types/extensions.ts @@ -1 +1 @@ -export type TExtensions = "ai" | "collaboration-cursor" | "issue-embed"; +export type TExtensions = "ai" | "collaboration-cursor" | "issue-embed" | "slash-commands"| "enter-key"; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index 292dc53fb..ed7d91346 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -1,10 +1,10 @@ // styles // import "./styles/tailwind.css"; -import "src/styles/variables.css"; -import "src/styles/editor.css"; -import "src/styles/table.css"; -import "src/styles/github-dark.css"; -import "src/styles/drag-drop.css"; +import "./styles/variables.css"; +import "./styles/editor.css"; +import "./styles/table.css"; +import "./styles/github-dark.css"; +import "./styles/drag-drop.css"; // editors export { diff --git a/packages/editor/src/styles/editor.css b/packages/editor/src/styles/editor.css index 05c9dc145..8ee5f8605 100644 --- a/packages/editor/src/styles/editor.css +++ b/packages/editor/src/styles/editor.css @@ -1,3 +1,7 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + .ProseMirror { position: relative; word-wrap: break-word; diff --git a/space/helpers/editor.helper.ts b/space/helpers/editor.helper.ts index 648e409e7..b037055af 100644 --- a/space/helpers/editor.helper.ts +++ b/space/helpers/editor.helper.ts @@ -31,7 +31,7 @@ export const getEditorFileHandlers = (args: TArgs): TFileHandler => { const { anchor, uploadFile, workspaceId } = args; return { - getAssetSrc: (path) => { + getAssetSrc: async (path) => { if (!path) return ""; if (path?.startsWith("http")) { return path; @@ -70,7 +70,7 @@ export const getReadOnlyEditorFileHandlers = ( const { anchor } = args; return { - getAssetSrc: (path) => { + getAssetSrc: async (path) => { if (!path) return ""; if (path?.startsWith("http")) { return path; diff --git a/web/helpers/editor.helper.ts b/web/helpers/editor.helper.ts index c84628132..e0cbef2e1 100644 --- a/web/helpers/editor.helper.ts +++ b/web/helpers/editor.helper.ts @@ -43,7 +43,7 @@ export const getEditorFileHandlers = (args: TArgs): TFileHandler => { const { maxFileSize, projectId, uploadFile, workspaceId, workspaceSlug } = args; return { - getAssetSrc: (path) => { + getAssetSrc: async (path) => { if (!path) return ""; if (path?.startsWith("http")) { return path; @@ -94,7 +94,7 @@ export const getReadOnlyEditorFileHandlers = ( const { projectId, workspaceSlug } = args; return { - getAssetSrc: (path) => { + getAssetSrc: async (path) => { if (!path) return ""; if (path?.startsWith("http")) { return path;