From ebc474df4850989506506404b2d34612b7a9d7e6 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Thu, 10 Jul 2025 15:07:45 +0530 Subject: [PATCH 1/8] refactor: document editor --- .../components/editor/document/editor.tsx | 100 +++++++++++++++++ apps/web/core/components/editor/index.ts | 4 +- .../editor/lite-text-editor/index.ts | 3 - .../editor.tsx} | 0 .../core/components/editor/lite-text/index.ts | 3 + .../read-only-editor.tsx} | 0 .../toolbar.tsx | 0 .../editor/rich-text-editor/index.ts | 1 - .../editor.tsx} | 0 .../core/components/editor/rich-text/index.ts | 1 + .../modals/create-modal/issue-description.tsx | 2 +- .../core/components/pages/version/editor.tsx | 49 ++------ .../src/ce/extensions/document-extensions.tsx | 1 + .../components/editors/document/editor.tsx | 105 ++++++++++++++++++ .../core/components/editors/document/index.ts | 2 +- .../editors/document/read-only-editor.tsx | 77 ------------- packages/editor/src/core/hooks/use-editor.ts | 2 +- packages/editor/src/core/types/editor.ts | 16 ++- packages/editor/src/core/types/hook.ts | 3 +- packages/editor/src/index.ts | 2 +- 20 files changed, 239 insertions(+), 132 deletions(-) create mode 100644 apps/web/core/components/editor/document/editor.tsx delete mode 100644 apps/web/core/components/editor/lite-text-editor/index.ts rename apps/web/core/components/editor/{lite-text-editor/lite-text-editor.tsx => lite-text/editor.tsx} (100%) create mode 100644 apps/web/core/components/editor/lite-text/index.ts rename apps/web/core/components/editor/{lite-text-editor/lite-text-read-only-editor.tsx => lite-text/read-only-editor.tsx} (100%) rename apps/web/core/components/editor/{lite-text-editor => lite-text}/toolbar.tsx (100%) delete mode 100644 apps/web/core/components/editor/rich-text-editor/index.ts rename apps/web/core/components/editor/{rich-text-editor/rich-text-editor.tsx => rich-text/editor.tsx} (100%) create mode 100644 apps/web/core/components/editor/rich-text/index.ts create mode 100644 packages/editor/src/core/components/editors/document/editor.tsx delete mode 100644 packages/editor/src/core/components/editors/document/read-only-editor.tsx diff --git a/apps/web/core/components/editor/document/editor.tsx b/apps/web/core/components/editor/document/editor.tsx new file mode 100644 index 00000000000..1abde3ef3ef --- /dev/null +++ b/apps/web/core/components/editor/document/editor.tsx @@ -0,0 +1,100 @@ +import React, { forwardRef, useMemo } from "react"; +// plane imports +import { DocumentEditorWithRef, EditorRefApi, IDocumentEditorProps, TFileHandler } from "@plane/editor"; +import { MakeOptional, TSearchEntityRequestPayload, TSearchResponse } from "@plane/types"; +import { cn, generateRandomColor, hslToHex } from "@plane/utils"; +// components +import { EditorMentionsRoot } from "@/components/editor"; +// hooks +import { useEditorConfig, useEditorMention } from "@/hooks/editor"; +import { useMember, useUser } from "@/hooks/store"; +// plane web hooks +import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; +import { useIssueEmbed } from "@/plane-web/hooks/use-issue-embed"; + +type DocumentEditorWrapperProps = MakeOptional< + Omit, + "disabledExtensions" | "editable" | "flaggedExtensions" +> & { + workspaceSlug: string; + workspaceId: string; + projectId?: string; +} & ( + | { + editable: false; + } + | { + editable: true; + searchMentionCallback: (payload: TSearchEntityRequestPayload) => Promise; + uploadFile: TFileHandler["upload"]; + } + ); + +export const DocumentEditor = forwardRef((props, ref) => { + const { + containerClassName, + editable, + workspaceSlug, + workspaceId, + projectId, + disabledExtensions: additionalDisabledExtensions, + ...rest + } = props; + // store hooks + const { data: currentUser } = useUser(); + const { getUserDetails } = useMember(); + // editor flaggings + const { document: documentEditorExtensions } = useEditorFlagging(workspaceSlug?.toString()); + // use editor mention + const { fetchMentions } = useEditorMention({ + searchEntity: editable ? async (payload) => await props.searchMentionCallback(payload) : async () => ({}), + }); + // editor config + const { getEditorFileHandlers } = useEditorConfig(); + // issue-embed + const { issueEmbedProps } = useIssueEmbed({ + projectId: projectId?.toString() ?? "", + workspaceSlug: workspaceSlug?.toString() ?? "", + }); + // user config + const userConfig = useMemo( + () => ({ + id: currentUser?.id ?? "", + name: currentUser?.display_name ?? "", + color: hslToHex(generateRandomColor(currentUser?.id ?? "")), + }), + [currentUser?.display_name, currentUser?.id] + ); + + return ( + "", + workspaceId, + workspaceSlug, + })} + mentionHandler={{ + searchCallback: async (query) => { + const res = await fetchMentions(query); + if (!res) throw new Error("Failed in fetching mentions"); + return res; + }, + renderComponent: (props) => , + getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }), + }} + embedHandler={{ + issue: issueEmbedProps, + }} + user={userConfig} + {...rest} + containerClassName={cn("relative pl-3 pb-3", containerClassName)} + /> + ); +}); + +DocumentEditor.displayName = "DocumentEditor"; diff --git a/apps/web/core/components/editor/index.ts b/apps/web/core/components/editor/index.ts index 674bbdf1582..c80c733527f 100644 --- a/apps/web/core/components/editor/index.ts +++ b/apps/web/core/components/editor/index.ts @@ -1,5 +1,5 @@ export * from "./embeds"; -export * from "./lite-text-editor"; +export * from "./lite-text"; export * from "./pdf"; -export * from "./rich-text-editor"; +export * from "./rich-text"; export * from "./sticky-editor"; diff --git a/apps/web/core/components/editor/lite-text-editor/index.ts b/apps/web/core/components/editor/lite-text-editor/index.ts deleted file mode 100644 index 661c8e75541..00000000000 --- a/apps/web/core/components/editor/lite-text-editor/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./lite-text-editor"; -export * from "./lite-text-read-only-editor"; -export * from "./toolbar"; diff --git a/apps/web/core/components/editor/lite-text-editor/lite-text-editor.tsx b/apps/web/core/components/editor/lite-text/editor.tsx similarity index 100% rename from apps/web/core/components/editor/lite-text-editor/lite-text-editor.tsx rename to apps/web/core/components/editor/lite-text/editor.tsx diff --git a/apps/web/core/components/editor/lite-text/index.ts b/apps/web/core/components/editor/lite-text/index.ts new file mode 100644 index 00000000000..a53feeb8277 --- /dev/null +++ b/apps/web/core/components/editor/lite-text/index.ts @@ -0,0 +1,3 @@ +export * from "./editor"; +export * from "./read-only-editor"; +export * from "./toolbar"; diff --git a/apps/web/core/components/editor/lite-text-editor/lite-text-read-only-editor.tsx b/apps/web/core/components/editor/lite-text/read-only-editor.tsx similarity index 100% rename from apps/web/core/components/editor/lite-text-editor/lite-text-read-only-editor.tsx rename to apps/web/core/components/editor/lite-text/read-only-editor.tsx diff --git a/apps/web/core/components/editor/lite-text-editor/toolbar.tsx b/apps/web/core/components/editor/lite-text/toolbar.tsx similarity index 100% rename from apps/web/core/components/editor/lite-text-editor/toolbar.tsx rename to apps/web/core/components/editor/lite-text/toolbar.tsx diff --git a/apps/web/core/components/editor/rich-text-editor/index.ts b/apps/web/core/components/editor/rich-text-editor/index.ts deleted file mode 100644 index 49fdb69dd1c..00000000000 --- a/apps/web/core/components/editor/rich-text-editor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./rich-text-editor"; diff --git a/apps/web/core/components/editor/rich-text-editor/rich-text-editor.tsx b/apps/web/core/components/editor/rich-text/editor.tsx similarity index 100% rename from apps/web/core/components/editor/rich-text-editor/rich-text-editor.tsx rename to apps/web/core/components/editor/rich-text/editor.tsx diff --git a/apps/web/core/components/editor/rich-text/index.ts b/apps/web/core/components/editor/rich-text/index.ts new file mode 100644 index 00000000000..8b1fd904bb0 --- /dev/null +++ b/apps/web/core/components/editor/rich-text/index.ts @@ -0,0 +1 @@ +export * from "./editor"; diff --git a/apps/web/core/components/inbox/modals/create-modal/issue-description.tsx b/apps/web/core/components/inbox/modals/create-modal/issue-description.tsx index 907f48e700f..09679cf313f 100644 --- a/apps/web/core/components/inbox/modals/create-modal/issue-description.tsx +++ b/apps/web/core/components/inbox/modals/create-modal/issue-description.tsx @@ -10,7 +10,7 @@ import { EFileAssetType, TIssue } from "@plane/types"; import { Loader } from "@plane/ui"; import { getDescriptionPlaceholderI18n, getTabIndex } from "@plane/utils"; // components -import { RichTextEditor } from "@/components/editor/rich-text-editor/rich-text-editor"; +import { RichTextEditor } from "@/components/editor/rich-text/editor"; // hooks import { useEditorAsset, useProjectInbox } from "@/hooks/store"; import { usePlatformOS } from "@/hooks/use-platform-os"; diff --git a/apps/web/core/components/pages/version/editor.tsx b/apps/web/core/components/pages/version/editor.tsx index 1a2c23e2872..8a5aa8d265f 100644 --- a/apps/web/core/components/pages/version/editor.tsx +++ b/apps/web/core/components/pages/version/editor.tsx @@ -1,18 +1,14 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // plane imports -import { DocumentReadOnlyEditorWithRef, TDisplayConfig } from "@plane/editor"; +import { TDisplayConfig } from "@plane/editor"; import { TPageVersion } from "@plane/types"; import { Loader } from "@plane/ui"; // components -import { EditorMentionsRoot } from "@/components/editor"; +import { DocumentEditor } from "@/components/editor/document/editor"; // hooks -import { useEditorConfig } from "@/hooks/editor"; -import { useMember, useWorkspace } from "@/hooks/store"; +import { useWorkspace } from "@/hooks/store"; import { usePageFilters } from "@/hooks/use-page-filters"; -// plane web hooks -import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; -import { useIssueEmbed } from "@/plane-web/hooks/use-issue-embed"; export type TVersionEditorProps = { activeVersion: string | null; @@ -21,23 +17,12 @@ export type TVersionEditorProps = { export const PagesVersionEditor: React.FC = observer((props) => { const { activeVersion, versionDetails } = props; - // store hooks - const { getUserDetails } = useMember(); // params const { workspaceSlug, projectId } = useParams(); // store hooks const { getWorkspaceBySlug } = useWorkspace(); // derived values const workspaceDetails = getWorkspaceBySlug(workspaceSlug?.toString() ?? ""); - // editor flaggings - const { document: documentEditorExtensions } = useEditorFlagging(workspaceSlug?.toString() ?? ""); - // editor config - const { getReadOnlyEditorFileHandlers } = useEditorConfig(); - // issue-embed - const { issueEmbedProps } = useIssueEmbed({ - projectId: projectId?.toString() ?? "", - workspaceSlug: workspaceSlug?.toString() ?? "", - }); // page filters const { fontSize, fontStyle } = usePageFilters(); @@ -89,32 +74,20 @@ export const PagesVersionEditor: React.FC = observer((props ); - const description = versionDetails?.description_html; - if (description === undefined || description?.trim() === "") return null; + const description = versionDetails?.description_json; + if (!description) return null; return ( -

"} + initialValue={description} containerClassName="p-0 pb-64 border-none" - disabledExtensions={documentEditorExtensions.disabled} - flaggedExtensions={documentEditorExtensions.flagged} displayConfig={displayConfig} editorClassName="pl-10" - fileHandler={getReadOnlyEditorFileHandlers({ - projectId: projectId?.toString() ?? "", - workspaceId: workspaceDetails?.id ?? "", - workspaceSlug: workspaceSlug?.toString() ?? "", - })} - mentionHandler={{ - renderComponent: (props) => , - getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }), - }} - embedHandler={{ - issue: { - widgetCallback: issueEmbedProps.widgetCallback, - }, - }} + projectId={projectId?.toString() ?? ""} + workspaceId={workspaceDetails?.id ?? ""} + workspaceSlug={workspaceSlug?.toString() ?? ""} /> ); }); diff --git a/packages/editor/src/ce/extensions/document-extensions.tsx b/packages/editor/src/ce/extensions/document-extensions.tsx index 8815e2d26e9..8260da7d434 100644 --- a/packages/editor/src/ce/extensions/document-extensions.tsx +++ b/packages/editor/src/ce/extensions/document-extensions.tsx @@ -11,6 +11,7 @@ export type TDocumentEditorAdditionalExtensionsProps = Pick< "disabledExtensions" | "flaggedExtensions" | "fileHandler" > & { embedConfig: TEmbedConfig | undefined; + isEditable: boolean; provider?: HocuspocusProvider; userDetails: TUserDetails; }; diff --git a/packages/editor/src/core/components/editors/document/editor.tsx b/packages/editor/src/core/components/editors/document/editor.tsx new file mode 100644 index 00000000000..42868c6c4c1 --- /dev/null +++ b/packages/editor/src/core/components/editors/document/editor.tsx @@ -0,0 +1,105 @@ +import { Extensions } from "@tiptap/core"; +import { forwardRef, MutableRefObject, useMemo } from "react"; +// plane imports +import { cn } from "@plane/utils"; +// components +import { PageRenderer } from "@/components/editors"; +// constants +import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config"; +// extensions +import { HeadingListExtension, WorkItemEmbedExtension, SideMenuExtension } from "@/extensions"; +// helpers +import { getEditorClassNames } from "@/helpers/common"; +// hooks +import { useEditor } from "@/hooks/use-editor"; +// plane editor extensions +import { DocumentEditorAdditionalExtensions } from "@/plane-editor/extensions"; +// types +import { EditorRefApi, IDocumentEditorProps } from "@/types"; + +const DocumentEditor = (props: IDocumentEditorProps) => { + const { + bubbleMenuEnabled = false, + containerClassName, + disabledExtensions, + displayConfig = DEFAULT_DISPLAY_CONFIG, + editable, + editorClassName = "", + embedHandler, + fileHandler, + flaggedExtensions, + forwardedRef, + id, + handleEditorReady, + initialValue, + mentionHandler, + onChange, + user, + } = props; + const extensions: Extensions = useMemo(() => { + const additionalExtensions: Extensions = []; + if (embedHandler?.issue) { + additionalExtensions.push( + WorkItemEmbedExtension({ + widgetCallback: embedHandler.issue.widgetCallback, + }) + ); + } + additionalExtensions.push( + SideMenuExtension({ + aiEnabled: !disabledExtensions?.includes("ai"), + dragDropEnabled: true, + }), + HeadingListExtension, + ...DocumentEditorAdditionalExtensions({ + disabledExtensions, + embedConfig: embedHandler, + flaggedExtensions, + isEditable: editable, + fileHandler, + userDetails: user, + }) + ); + return additionalExtensions; + }, []); + + const editor = useEditor({ + disabledExtensions, + editable, + editorClassName, + enableHistory: true, + extensions, + fileHandler, + flaggedExtensions, + forwardedRef, + handleEditorReady, + id, + initialValue, + mentionHandler, + onChange, + }); + + const editorContainerClassName = getEditorClassNames({ + containerClassName, + }); + + if (!editor) return null; + + return ( + + ); +}; + +const DocumentEditorWithRef = forwardRef((props, ref) => ( + } /> +)); + +DocumentEditorWithRef.displayName = "DocumentEditorWithRef"; + +export { DocumentEditorWithRef }; diff --git a/packages/editor/src/core/components/editors/document/index.ts b/packages/editor/src/core/components/editors/document/index.ts index 571cb7e9a1f..8a5bffd178b 100644 --- a/packages/editor/src/core/components/editors/document/index.ts +++ b/packages/editor/src/core/components/editors/document/index.ts @@ -1,4 +1,4 @@ export * from "./collaborative-editor"; +export * from "./editor"; export * from "./loader"; export * from "./page-renderer"; -export * from "./read-only-editor"; diff --git a/packages/editor/src/core/components/editors/document/read-only-editor.tsx b/packages/editor/src/core/components/editors/document/read-only-editor.tsx deleted file mode 100644 index 8f0d67ddc0d..00000000000 --- a/packages/editor/src/core/components/editors/document/read-only-editor.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Extensions } from "@tiptap/core"; -import React, { forwardRef, MutableRefObject } from "react"; -// plane imports -import { cn } from "@plane/utils"; -// components -import { PageRenderer } from "@/components/editors"; -// constants -import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config"; -// extensions -import { WorkItemEmbedExtension } from "@/extensions"; -// helpers -import { getEditorClassNames } from "@/helpers/common"; -// hooks -import { useReadOnlyEditor } from "@/hooks/use-read-only-editor"; -// types -import { EditorReadOnlyRefApi, IDocumentReadOnlyEditorProps } from "@/types"; - -const DocumentReadOnlyEditor: React.FC = (props) => { - const { - containerClassName, - disabledExtensions, - displayConfig = DEFAULT_DISPLAY_CONFIG, - editorClassName = "", - embedHandler, - fileHandler, - flaggedExtensions, - id, - forwardedRef, - handleEditorReady, - initialValue, - mentionHandler, - } = props; - const extensions: Extensions = []; - if (embedHandler?.issue) { - extensions.push( - WorkItemEmbedExtension({ - widgetCallback: embedHandler.issue.widgetCallback, - }) - ); - } - - const editor = useReadOnlyEditor({ - disabledExtensions, - editorClassName, - extensions, - fileHandler, - flaggedExtensions, - forwardedRef, - handleEditorReady, - initialValue, - mentionHandler, - }); - - const editorContainerClassName = getEditorClassNames({ - containerClassName, - }); - - if (!editor) return null; - - return ( - - ); -}; - -const DocumentReadOnlyEditorWithRef = forwardRef((props, ref) => ( - } /> -)); - -DocumentReadOnlyEditorWithRef.displayName = "DocumentReadOnlyEditorWithRef"; - -export { DocumentReadOnlyEditorWithRef }; diff --git a/packages/editor/src/core/hooks/use-editor.ts b/packages/editor/src/core/hooks/use-editor.ts index 1979d46b14b..a61b4172fee 100644 --- a/packages/editor/src/core/hooks/use-editor.ts +++ b/packages/editor/src/core/hooks/use-editor.ts @@ -70,7 +70,7 @@ export const useEditor = (props: TEditorHookProps) => { }), ...extensions, ], - content: typeof initialValue === "string" && initialValue.trim() !== "" ? initialValue : "

", + content: initialValue, onCreate: () => handleEditorReady?.(true), onTransaction: () => { onTransaction?.(); diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index d4d572502fe..3ff99ba8fa1 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -1,5 +1,5 @@ -import { Extensions, JSONContent } from "@tiptap/core"; -import { Selection } from "@tiptap/pm/state"; +import type { Content, Extensions, JSONContent } from "@tiptap/core"; +import type { Selection } from "@tiptap/pm/state"; // extension types import type { TTextAlign } from "@/extensions"; // helpers @@ -159,6 +159,14 @@ export interface ICollaborativeDocumentEditorProps user: TUserDetails; } +export interface IDocumentEditorProps extends Omit { + aiHandler?: TAIHandler; + editable: boolean; + embedHandler: TEmbedConfig; + initialValue: Content; + user: TUserDetails; +} + // read only editor props export interface IReadOnlyEditorProps extends Pick< @@ -180,10 +188,6 @@ export interface IReadOnlyEditorProps export type ILiteTextReadOnlyEditorProps = IReadOnlyEditorProps; -export interface IDocumentReadOnlyEditorProps extends IReadOnlyEditorProps { - embedHandler: TEmbedConfig; -} - export interface EditorEvents { beforeCreate: never; create: never; diff --git a/packages/editor/src/core/types/hook.ts b/packages/editor/src/core/types/hook.ts index 40974981b7d..fa014ceb905 100644 --- a/packages/editor/src/core/types/hook.ts +++ b/packages/editor/src/core/types/hook.ts @@ -1,4 +1,5 @@ import type { HocuspocusProvider } from "@hocuspocus/provider"; +import type { Content } from "@tiptap/core"; import type { EditorProps } from "@tiptap/pm/view"; // local imports import type { ICollaborativeDocumentEditorProps, IEditorProps, IReadOnlyEditorProps } from "./editor"; @@ -27,7 +28,7 @@ export type TEditorHookProps = TCoreHookProps & > & { editable: boolean; enableHistory: boolean; - initialValue?: string; + initialValue?: Content; provider?: HocuspocusProvider; }; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index 5484d0affaf..2177b944b9e 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -9,7 +9,7 @@ import "./styles/drag-drop.css"; // editors export { CollaborativeDocumentEditorWithRef, - DocumentReadOnlyEditorWithRef, + DocumentEditorWithRef, LiteTextEditorWithRef, LiteTextReadOnlyEditorWithRef, RichTextEditorWithRef, From eebf3843e7cf21dfeea12448505b28de308856ef Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Thu, 10 Jul 2025 17:45:31 +0530 Subject: [PATCH 2/8] chore: update user prop --- .../editor/src/core/components/editors/document/editor.tsx | 6 +++++- packages/editor/src/core/types/editor.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/core/components/editors/document/editor.tsx b/packages/editor/src/core/components/editors/document/editor.tsx index 42868c6c4c1..a48207dd61a 100644 --- a/packages/editor/src/core/components/editors/document/editor.tsx +++ b/packages/editor/src/core/components/editors/document/editor.tsx @@ -57,7 +57,11 @@ const DocumentEditor = (props: IDocumentEditorProps) => { flaggedExtensions, isEditable: editable, fileHandler, - userDetails: user, + userDetails: user ?? { + id: "", + name: "", + color: "", + }, }) ); return additionalExtensions; diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index 3ff99ba8fa1..dee93ec8450 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -164,7 +164,7 @@ export interface IDocumentEditorProps extends Omit Date: Thu, 10 Jul 2025 17:50:58 +0530 Subject: [PATCH 3/8] fix: type warning --- packages/editor/src/core/helpers/scroll-to-node.ts | 1 + packages/editor/src/core/types/editor.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/editor/src/core/helpers/scroll-to-node.ts b/packages/editor/src/core/helpers/scroll-to-node.ts index 973f3cf141b..7e5aa0979a0 100644 --- a/packages/editor/src/core/helpers/scroll-to-node.ts +++ b/packages/editor/src/core/helpers/scroll-to-node.ts @@ -32,6 +32,7 @@ function scrollToNode(editor: Editor, pos: number): void { } } +// eslint-disable-next-line no-undef export function scrollToNodeViaDOMCoordinates(editor: Editor, pos: number, behavior?: ScrollBehavior): void { const view = editor.view; diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index dee93ec8450..9e453b32e5e 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -111,6 +111,7 @@ export interface EditorRefApi extends EditorReadOnlyRefApi { onDocumentInfoChange: (callback: (documentInfo: TDocumentInfo) => void) => () => void; onHeadingChange: (callback: (headings: IMarking[]) => void) => () => void; onStateChange: (callback: () => void) => () => void; + // eslint-disable-next-line no-undef scrollToNodeViaDOMCoordinates: (behavior?: ScrollBehavior, position?: number) => void; setEditorValueAtCursorPosition: (content: string) => void; setFocusAtPosition: (position: number) => void; From cee28b168b9d9f943765ae84efe49d04ab92b297 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Thu, 10 Jul 2025 18:27:05 +0530 Subject: [PATCH 4/8] chore: update value prop name --- .../components/editor/document/editor.tsx | 20 ++++++------------- .../core/components/pages/version/editor.tsx | 3 ++- .../components/editors/document/editor.tsx | 4 ++-- packages/editor/src/core/types/editor.ts | 2 +- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/apps/web/core/components/editor/document/editor.tsx b/apps/web/core/components/editor/document/editor.tsx index 1abde3ef3ef..edb2fe4f95e 100644 --- a/apps/web/core/components/editor/document/editor.tsx +++ b/apps/web/core/components/editor/document/editor.tsx @@ -1,13 +1,13 @@ -import React, { forwardRef, useMemo } from "react"; +import React, { forwardRef } from "react"; // plane imports import { DocumentEditorWithRef, EditorRefApi, IDocumentEditorProps, TFileHandler } from "@plane/editor"; import { MakeOptional, TSearchEntityRequestPayload, TSearchResponse } from "@plane/types"; -import { cn, generateRandomColor, hslToHex } from "@plane/utils"; +import { cn } from "@plane/utils"; // components import { EditorMentionsRoot } from "@/components/editor"; // hooks import { useEditorConfig, useEditorMention } from "@/hooks/editor"; -import { useMember, useUser } from "@/hooks/store"; +import { useMember } from "@/hooks/store"; // plane web hooks import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging"; import { useIssueEmbed } from "@/plane-web/hooks/use-issue-embed"; @@ -16,6 +16,7 @@ type DocumentEditorWrapperProps = MakeOptional< Omit, "disabledExtensions" | "editable" | "flaggedExtensions" > & { + embedHandler?: Partial; workspaceSlug: string; workspaceId: string; projectId?: string; @@ -34,6 +35,7 @@ export const DocumentEditor = forwardRef ({ - id: currentUser?.id ?? "", - name: currentUser?.display_name ?? "", - color: hslToHex(generateRandomColor(currentUser?.id ?? "")), - }), - [currentUser?.display_name, currentUser?.id] - ); return ( diff --git a/apps/web/core/components/pages/version/editor.tsx b/apps/web/core/components/pages/version/editor.tsx index 8a5aa8d265f..3d4680332a4 100644 --- a/apps/web/core/components/pages/version/editor.tsx +++ b/apps/web/core/components/pages/version/editor.tsx @@ -79,9 +79,10 @@ export const PagesVersionEditor: React.FC = observer((props return ( { forwardedRef, id, handleEditorReady, - initialValue, mentionHandler, onChange, user, + value, } = props; const extensions: Extensions = useMemo(() => { const additionalExtensions: Extensions = []; @@ -78,7 +78,7 @@ const DocumentEditor = (props: IDocumentEditorProps) => { forwardedRef, handleEditorReady, id, - initialValue, + initialValue: value, mentionHandler, onChange, }); diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index 9e453b32e5e..d511bb7f122 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -164,8 +164,8 @@ export interface IDocumentEditorProps extends Omit Date: Thu, 10 Jul 2025 18:46:29 +0530 Subject: [PATCH 5/8] chore: remove unnecessary exports --- .../src/core/hooks/use-editor-markings.tsx | 39 ------------------- packages/editor/src/core/types/config.ts | 7 ++++ packages/editor/src/index.ts | 12 ------ 3 files changed, 7 insertions(+), 51 deletions(-) delete mode 100644 packages/editor/src/core/hooks/use-editor-markings.tsx diff --git a/packages/editor/src/core/hooks/use-editor-markings.tsx b/packages/editor/src/core/hooks/use-editor-markings.tsx deleted file mode 100644 index 76d02cd68e2..00000000000 --- a/packages/editor/src/core/hooks/use-editor-markings.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useCallback, useState } from "react"; - -export interface IMarking { - type: "heading"; - level: number; - text: string; - sequence: number; -} - -export const useEditorMarkings = () => { - const [markings, setMarkings] = useState([]); - - const updateMarkings = useCallback((html: string) => { - const parser = new DOMParser(); - const doc = parser.parseFromString(html, "text/html"); - const headings = doc.querySelectorAll("h1, h2, h3"); - const tempMarkings: IMarking[] = []; - let h1Sequence: number = 0; - let h2Sequence: number = 0; - let h3Sequence: number = 0; - - headings.forEach((heading) => { - const level = parseInt(heading.tagName[1]); // Extract the number from h1, h2, h3 - tempMarkings.push({ - type: "heading", - level: level, - text: heading.textContent || "", - sequence: level === 1 ? ++h1Sequence : level === 2 ? ++h2Sequence : ++h3Sequence, - }); - }); - - setMarkings(tempMarkings); - }, []); - - return { - updateMarkings, - markings, - }; -}; diff --git a/packages/editor/src/core/types/config.ts b/packages/editor/src/core/types/config.ts index 7ef685ad02d..8c1903cf2dc 100644 --- a/packages/editor/src/core/types/config.ts +++ b/packages/editor/src/core/types/config.ts @@ -46,3 +46,10 @@ export type TRealtimeConfig = { url: string; queryParams: TWebhookConnectionQueryParams; }; + +export type IMarking = { + type: "heading"; + level: number; + text: string; + sequence: number; +}; diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index 2177b944b9e..2cfc6891588 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -15,24 +15,12 @@ export { RichTextEditorWithRef, } from "@/components/editors"; -export { isCellSelection } from "@/extensions/table/table/utilities/is-cell-selection"; - // constants export * from "@/constants/common"; // helpers export * from "@/helpers/common"; -export * from "@/helpers/editor-commands"; export * from "@/helpers/yjs-utils"; -export * from "@/extensions/table/table"; - -// components -export * from "@/components/menus"; - -// hooks -export { useEditor } from "@/hooks/use-editor"; -export { type IMarking, useEditorMarkings } from "@/hooks/use-editor-markings"; -export { useReadOnlyEditor } from "@/hooks/use-read-only-editor"; export { CORE_EXTENSIONS } from "@/constants/extension"; export { ADDITIONAL_EXTENSIONS } from "@/plane-editor/constants/extensions"; From 7136cc3e56095dc6e3b2c8d9bbe0866f93ae4f97 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Thu, 10 Jul 2025 18:51:37 +0530 Subject: [PATCH 6/8] hore: update initialValue type --- packages/editor/src/core/hooks/use-collaborative-editor.ts | 1 + packages/editor/src/core/types/editor.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/core/hooks/use-collaborative-editor.ts b/packages/editor/src/core/hooks/use-collaborative-editor.ts index 3b4b333e6af..e6c20b13f7b 100644 --- a/packages/editor/src/core/hooks/use-collaborative-editor.ts +++ b/packages/editor/src/core/hooks/use-collaborative-editor.ts @@ -98,6 +98,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorHookProps) => embedConfig: embedHandler, fileHandler, flaggedExtensions, + isEditable: editable, provider, userDetails: user, }), diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index d511bb7f122..a83363d588b 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -132,7 +132,7 @@ export interface IEditorProps { forwardedRef?: React.MutableRefObject; handleEditorReady?: (value: boolean) => void; id: string; - initialValue: string; + initialValue: Content; mentionHandler: TMentionHandler; onAssetChange?: (assets: TEditorAsset[]) => void; onChange?: (json: object, html: string) => void; From 65e38ab5597cb134751a991fb4d9a7a5b4d9fbf4 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Thu, 10 Jul 2025 19:00:45 +0530 Subject: [PATCH 7/8] chore: revert initialValue type --- packages/editor/src/core/types/editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/core/types/editor.ts b/packages/editor/src/core/types/editor.ts index a83363d588b..d511bb7f122 100644 --- a/packages/editor/src/core/types/editor.ts +++ b/packages/editor/src/core/types/editor.ts @@ -132,7 +132,7 @@ export interface IEditorProps { forwardedRef?: React.MutableRefObject; handleEditorReady?: (value: boolean) => void; id: string; - initialValue: Content; + initialValue: string; mentionHandler: TMentionHandler; onAssetChange?: (assets: TEditorAsset[]) => void; onChange?: (json: object, html: string) => void; From 745d4c700f10536fdd9a240396ebd095141e468d Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Fri, 18 Jul 2025 14:48:04 +0530 Subject: [PATCH 8/8] refactor: unnecessary string handlers --- apps/web/core/components/editor/document/editor.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/core/components/editor/document/editor.tsx b/apps/web/core/components/editor/document/editor.tsx index edb2fe4f95e..a579430d9a1 100644 --- a/apps/web/core/components/editor/document/editor.tsx +++ b/apps/web/core/components/editor/document/editor.tsx @@ -39,13 +39,13 @@ export const DocumentEditor = forwardRef await props.searchMentionCallback(payload) : async () => ({}), @@ -54,8 +54,8 @@ export const DocumentEditor = forwardRef , + renderComponent: EditorMentionsRoot, getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }), }} embedHandler={{