Compare commits

...

2 Commits

Author SHA1 Message Date
Aaryan Khandelwal 015f8cbfe5 chore: split editor ref 2025-08-26 16:56:29 +05:30
Aaryan Khandelwal 42b38db131 chore: implement code splitting for editor 2025-08-26 14:37:17 +05:30
27 changed files with 7588 additions and 9396 deletions
@@ -0,0 +1 @@
export const DRAG_HANDLE_ADDITIONAL_SELECTORS = [];
@@ -1,8 +1,6 @@
import type { HocuspocusProvider } from "@hocuspocus/provider";
import type { AnyExtension } from "@tiptap/core";
import { SlashCommands } from "@/extensions";
// plane editor types
import type { TEmbedConfig } from "@/plane-editor/types";
// types
import type { IEditorProps, TExtensions, TUserDetails } from "@/types";
@@ -10,7 +8,6 @@ export type TDocumentEditorAdditionalExtensionsProps = Pick<
IEditorProps,
"disabledExtensions" | "flaggedExtensions" | "fileHandler"
> & {
embedConfig: TEmbedConfig | undefined;
isEditable: boolean;
provider?: HocuspocusProvider;
userDetails: TUserDetails;
@@ -0,0 +1,6 @@
// helpers
import type { TEditorRefHelperArgs } from "@/helpers/editor-ref";
// local imports
import type { TAdditionalEditorRefApiMethods } from "../types/editor";
export const getAdditionalEditorRefHelpers = (_args: TEditorRefHelperArgs): TAdditionalEditorRefApiMethods => ({});
+7
View File
@@ -0,0 +1,7 @@
export type TAdditionalEditorCommands = never;
export type TAdditionalCommandExtraProps = {};
export type TAdditionalEditorRefApiMethods = {};
export type IEditorPropsExtended = {};
-1
View File
@@ -1 +0,0 @@
export * from "./issue-embed";
@@ -1,17 +0,0 @@
export type TEmbedConfig = {
issue?: TIssueEmbedConfig;
};
export type TReadOnlyEmbedConfig = TEmbedConfig;
export type TIssueEmbedConfig = {
widgetCallback: ({
issueId,
projectId,
workspaceSlug,
}: {
issueId: string;
projectId: string | undefined;
workspaceSlug: string | undefined;
}) => React.ReactNode;
};
@@ -6,8 +6,6 @@ import { cn } from "@plane/utils";
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
@@ -21,13 +19,12 @@ const CollaborativeDocumentEditor: React.FC<ICollaborativeDocumentEditorProps> =
bubbleMenuEnabled = true,
containerClassName,
documentLoaderClassName,
extensions: externalExtensions = [],
extensions,
disabledExtensions,
displayConfig = DEFAULT_DISPLAY_CONFIG,
editable,
editorClassName = "",
editorProps,
embedHandler,
fileHandler,
flaggedExtensions,
forwardedRef,
@@ -47,27 +44,12 @@ const CollaborativeDocumentEditor: React.FC<ICollaborativeDocumentEditorProps> =
user,
} = props;
const extensions: Extensions = useMemo(() => {
const allExtensions = [...externalExtensions];
if (embedHandler?.issue) {
allExtensions.push(
WorkItemEmbedExtension({
widgetCallback: embedHandler.issue.widgetCallback,
})
);
}
return allExtensions;
}, [externalExtensions, embedHandler.issue]);
// use document editor
const { editor, hasServerConnectionFailed, hasServerSynced } = useCollaborativeEditor({
disabledExtensions,
editable,
editorClassName,
editorProps,
embedHandler,
extensions,
fileHandler,
flaggedExtensions,
@@ -7,7 +7,7 @@ import { PageRenderer } from "@/components/editors";
// constants
import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config";
// extensions
import { HeadingListExtension, WorkItemEmbedExtension, SideMenuExtension } from "@/extensions";
import { HeadingListExtension, SideMenuExtension } from "@/extensions";
// helpers
import { getEditorClassNames } from "@/helpers/common";
// hooks
@@ -25,7 +25,6 @@ const DocumentEditor = (props: IDocumentEditorProps) => {
displayConfig = DEFAULT_DISPLAY_CONFIG,
editable,
editorClassName = "",
embedHandler,
fileHandler,
flaggedExtensions,
forwardedRef,
@@ -39,13 +38,6 @@ const DocumentEditor = (props: IDocumentEditorProps) => {
} = 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"),
@@ -54,7 +46,6 @@ const DocumentEditor = (props: IDocumentEditorProps) => {
HeadingListExtension,
...DocumentEditorAdditionalExtensions({
disabledExtensions,
embedConfig: embedHandler,
flaggedExtensions,
isEditable: editable,
fileHandler,
@@ -82,7 +73,6 @@ const DocumentEditor = (props: IDocumentEditorProps) => {
initialValue: value,
mentionHandler,
onChange,
embedHandler,
});
const editorContainerClassName = getEditorClassNames({
@@ -41,7 +41,6 @@ export const EditorWrapper: React.FC<Props> = (props) => {
placeholder,
tabIndex,
value,
embedHandler,
} = props;
const editor = useEditor({
@@ -66,7 +65,6 @@ export const EditorWrapper: React.FC<Props> = (props) => {
placeholder,
tabIndex,
value,
embedHandler,
});
const editorContainerClassName = getEditorClassNames({
@@ -21,7 +21,6 @@ import { CustomMentionExtensionConfig } from "./mentions/extension-config";
import { CustomQuoteExtension } from "./quote";
import { TableHeader, TableCell, TableRow, Table } from "./table";
import { CustomTextAlignExtension } from "./text-align";
import { WorkItemEmbedExtensionConfig } from "./work-item-embed/extension-config";
export const CoreEditorExtensionsWithoutProps = [
StarterKit.configure({
@@ -101,5 +100,3 @@ export const CoreEditorExtensionsWithoutProps = [
CustomColorExtension,
...CoreEditorAdditionalExtensionsWithoutProps,
];
export const DocumentEditorExtensionsWithoutProps = [WorkItemEmbedExtensionConfig];
@@ -29,7 +29,7 @@ import {
// plane editor extensions
import { CoreEditorAdditionalExtensions } from "@/plane-editor/extensions";
// types
import type { IEditorProps, TEmbedConfig } from "@/types";
import type { IEditorProps } from "@/types";
// local imports
import { CustomImageExtension } from "./custom-image/extension";
import { EmojiExtension } from "./emoji/extension";
@@ -45,11 +45,9 @@ type TArguments = Pick<
| "mentionHandler"
| "placeholder"
| "tabIndex"
| "embedHandler"
> & {
enableHistory: boolean;
editable: boolean;
embedHandler?: TEmbedConfig;
};
export const CoreEditorExtensions = (args: TArguments): Extensions => {
@@ -62,7 +60,6 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
mentionHandler,
placeholder,
tabIndex,
embedHandler,
editable,
} = args;
@@ -118,7 +115,6 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
disabledExtensions,
flaggedExtensions,
fileHandler,
embedHandler,
}),
];
@@ -8,7 +8,6 @@ export * from "./mentions";
export * from "./slash-commands";
export * from "./table";
export * from "./typography";
export * from "./work-item-embed";
export * from "./core-without-props";
export * from "./custom-color";
export * from "./enter-key";
@@ -1,43 +0,0 @@
import { mergeAttributes, Node } from "@tiptap/core";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
export const WorkItemEmbedExtensionConfig = Node.create({
name: CORE_EXTENSIONS.WORK_ITEM_EMBED,
group: "block",
atom: true,
selectable: true,
draggable: true,
addAttributes() {
return {
entity_identifier: {
default: undefined,
},
project_identifier: {
default: undefined,
},
workspace_identifier: {
default: undefined,
},
id: {
default: undefined,
},
entity_name: {
default: undefined,
},
};
},
parseHTML() {
return [
{
tag: "issue-embed-component",
},
];
},
renderHTML({ HTMLAttributes }) {
return ["issue-embed-component", mergeAttributes(HTMLAttributes)];
},
});
@@ -1,30 +0,0 @@
import { ReactNodeViewRenderer, NodeViewWrapper, type NodeViewProps } from "@tiptap/react";
// local imports
import { WorkItemEmbedExtensionConfig } from "./extension-config";
type Props = {
widgetCallback: ({
issueId,
projectId,
workspaceSlug,
}: {
issueId: string;
projectId: string | undefined;
workspaceSlug: string | undefined;
}) => React.ReactNode;
};
export const WorkItemEmbedExtension = (props: Props) =>
WorkItemEmbedExtensionConfig.extend({
addNodeView() {
return ReactNodeViewRenderer((issueProps: NodeViewProps) => (
<NodeViewWrapper>
{props.widgetCallback({
issueId: issueProps.node.attrs.entity_identifier,
projectId: issueProps.node.attrs.project_identifier,
workspaceSlug: issueProps.node.attrs.workspace_identifier,
})}
</NodeViewWrapper>
));
},
});
@@ -1 +0,0 @@
export * from "./extension";
@@ -9,18 +9,20 @@ import { CORE_EXTENSIONS } from "@/constants/extension";
import { CORE_EDITOR_META } from "@/constants/meta";
// types
import type { EditorRefApi, TEditorCommands } from "@/types";
// plane editor imports
import { getAdditionalEditorRefHelpers } from "@/plane-editor/helpers/editor-ref";
// local imports
import { getParagraphCount } from "./common";
import { getExtensionStorage } from "./get-extension-storage";
import { insertContentAtSavedSelection } from "./insert-content-at-cursor-position";
import { scrollSummary, scrollToNodeViaDOMCoordinates } from "./scroll-to-node";
type TArgs = {
export type TEditorRefHelperArgs = {
editor: Editor | null;
provider: HocuspocusProvider | undefined;
};
export const getEditorRefHelpers = (args: TArgs): EditorRefApi => {
export const getEditorRefHelpers = (args: TEditorRefHelperArgs): EditorRefApi => {
const { editor, provider } = args;
return {
@@ -238,5 +240,6 @@ export const getEditorRefHelpers = (args: TArgs): EditorRefApi => {
Y.applyUpdate(document, value);
},
undo: () => editor?.commands.undo(),
...getAdditionalEditorRefHelpers(args),
};
};
@@ -4,14 +4,11 @@ import { prosemirrorJSONToYDoc, yXmlFragmentToProseMirrorRootNode } from "y-pros
import * as Y from "yjs";
// extensions
import { TDocumentPayload } from "@plane/types";
import {
CoreEditorExtensionsWithoutProps,
DocumentEditorExtensionsWithoutProps,
} from "@/extensions/core-without-props";
import { CoreEditorExtensionsWithoutProps } from "@/extensions/core-without-props";
// editor extension configs
const RICH_TEXT_EDITOR_EXTENSIONS = CoreEditorExtensionsWithoutProps;
const DOCUMENT_EDITOR_EXTENSIONS = [...CoreEditorExtensionsWithoutProps, ...DocumentEditorExtensionsWithoutProps];
const DOCUMENT_EDITOR_EXTENSIONS = CoreEditorExtensionsWithoutProps;
// editor schemas
const richTextEditorSchema = getSchema(RICH_TEXT_EDITOR_EXTENSIONS);
const documentEditorSchema = getSchema(DOCUMENT_EDITOR_EXTENSIONS);
@@ -20,7 +20,6 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorHookProps) =>
editable,
editorClassName = "",
editorProps = {},
embedHandler,
extensions = [],
fileHandler,
flaggedExtensions,
@@ -80,7 +79,6 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorHookProps) =>
);
const editor = useEditor({
embedHandler,
disabledExtensions,
id,
editable,
@@ -99,7 +97,6 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorHookProps) =>
...extensions,
...DocumentEditorAdditionalExtensions({
disabledExtensions,
embedConfig: embedHandler,
fileHandler,
flaggedExtensions,
isEditable: editable,
@@ -32,7 +32,6 @@ export const useEditor = (props: TEditorHookProps) => {
onAssetChange,
onChange,
onEditorFocus,
embedHandler,
onTransaction,
placeholder,
provider,
@@ -64,7 +63,6 @@ export const useEditor = (props: TEditorHookProps) => {
mentionHandler,
placeholder,
tabIndex,
embedHandler,
}),
...extensions,
],
@@ -4,7 +4,9 @@ import { EditorView } from "@tiptap/pm/view";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
// extensions
import { SideMenuHandleOptions, SideMenuPluginProps } from "@/extensions";
import { SideMenuHandleOptions, SideMenuPluginProps } from "@/extensions/side-menu";
// plane editor imports
import { DRAG_HANDLE_ADDITIONAL_SELECTORS } from "@/plane-editor/constants/common";
const verticalEllipsisIcon =
'<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ellipsis-vertical"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>';
@@ -17,11 +19,10 @@ const generalSelectors = [
"h1.editor-heading-block, h2.editor-heading-block, h3.editor-heading-block, h4.editor-heading-block, h5.editor-heading-block, h6.editor-heading-block",
"[data-type=horizontalRule]",
"table:not(.table-drag-preview)",
".issue-embed",
".image-component",
".image-upload-component",
".editor-callout-component",
".editor-embed-component",
...DRAG_HANDLE_ADDITIONAL_SELECTORS,
].join(", ");
const maxScrollSpeed = 20;
+13 -18
View File
@@ -2,9 +2,15 @@ import type { Content, Extensions, JSONContent, RawCommands } from "@tiptap/core
import type { MarkType, NodeType } from "@tiptap/pm/model";
import type { Selection } from "@tiptap/pm/state";
import type { EditorProps, EditorView } from "@tiptap/pm/view";
import type { NodeViewProps as TNodeViewProps } from "@tiptap/react";
// extension types
import type { TTextAlign } from "@/extensions";
import type { TTextAlign } from "@/extensions/text-align";
// plane editor imports
import type {
IEditorPropsExtended,
TAdditionalCommandExtraProps,
TAdditionalEditorCommands,
TAdditionalEditorRefApiMethods,
} from "@/plane-editor/types/editor";
// types
import type {
IMarking,
@@ -13,7 +19,6 @@ import type {
TDocumentEventEmitter,
TDocumentEventsServer,
TEditorAsset,
TEmbedConfig,
TExtensions,
TFileHandler,
TMentionHandler,
@@ -43,22 +48,17 @@ export type TEditorCommands =
| "image"
| "divider"
| "link"
| "issue-embed"
| "text-color"
| "background-color"
| "text-align"
| "callout"
| "attachment"
| "emoji"
| "external-embed";
| TAdditionalEditorCommands;
export type TCommandExtraProps = {
image: {
savedSelection: Selection | null;
};
attachment: {
savedSelection: Selection | null;
};
"text-color": {
color: string | undefined;
};
@@ -72,7 +72,7 @@ export type TCommandExtraProps = {
"text-align": {
alignment: TTextAlign;
};
};
} & TAdditionalCommandExtraProps;
// Create a utility type that maps a command to its extra props or an empty object if none are defined
export type TCommandWithProps<T extends TEditorCommands> = T extends keyof TCommandExtraProps
@@ -127,7 +127,7 @@ export type EditorRefApi = {
setFocusAtPosition: (position: number) => void;
setProviderDocument: (value: Uint8Array) => void;
undo: () => void;
};
} & TAdditionalEditorRefApiMethods;
// editor props
export type IEditorProps = {
@@ -140,7 +140,6 @@ export type IEditorProps = {
editorClassName?: string;
editorProps?: EditorProps;
extensions?: Extensions;
embedHandler?: TEmbedConfig;
flaggedExtensions: TExtensions[];
fileHandler: TFileHandler;
forwardedRef?: React.MutableRefObject<EditorRefApi | null>;
@@ -150,14 +149,14 @@ export type IEditorProps = {
isTouchDevice?: boolean;
mentionHandler: TMentionHandler;
onAssetChange?: (assets: TEditorAsset[]) => void;
onEditorFocus?: () => void;
onChange?: (json: object, html: string) => void;
onEditorFocus?: () => void;
onEnterKeyPress?: (e?: any) => void;
onTransaction?: () => void;
placeholder?: string | ((isFocused: boolean, value: string) => string);
tabIndex?: number;
value?: string | null;
};
} & IEditorPropsExtended;
export type ILiteTextEditorProps = IEditorProps;
@@ -170,7 +169,6 @@ export type ICollaborativeDocumentEditorProps = Omit<IEditorProps, "initialValue
documentLoaderClassName?: string;
dragDropEnabled?: boolean;
editable: boolean;
embedHandler: TEmbedConfig;
realtimeConfig: TRealtimeConfig;
serverHandler?: TServerHandler;
user: TUserDetails;
@@ -178,7 +176,6 @@ export type ICollaborativeDocumentEditorProps = Omit<IEditorProps, "initialValue
export type IDocumentEditorProps = Omit<IEditorProps, "initialValue" | "onEnterKeyPress" | "value"> & {
aiHandler?: TAIHandler;
embedHandler: TEmbedConfig;
user?: TUserDetails;
value: Content;
};
@@ -194,5 +191,3 @@ export type EditorEvents = {
destroy: never;
ready: { height: number };
};
export type NodeViewProps = TNodeViewProps;
-8
View File
@@ -1,8 +0,0 @@
export type TEmbedItem = {
id: string;
title: string;
subTitle: string;
icon: React.ReactNode;
projectId: string;
workspaceSlug: string;
};
+1 -5
View File
@@ -13,7 +13,6 @@ type TCoreHookProps = Pick<
| "handleEditorReady"
| "isTouchDevice"
| "onEditorFocus"
| "embedHandler"
>;
export type TEditorHookProps = TCoreHookProps &
@@ -51,7 +50,4 @@ export type TCollaborativeEditorHookProps = TCoreHookProps &
| "placeholder"
| "tabIndex"
> &
Pick<
ICollaborativeDocumentEditorProps,
"dragDropEnabled" | "embedHandler" | "realtimeConfig" | "serverHandler" | "user"
>;
Pick<ICollaborativeDocumentEditorProps, "dragDropEnabled" | "realtimeConfig" | "serverHandler" | "user">;
-2
View File
@@ -3,10 +3,8 @@ export * from "./asset";
export * from "./collaboration";
export * from "./config";
export * from "./editor";
export * from "./embed";
export * from "./extensions";
export * from "./hook";
export * from "./mention";
export * from "./slash-commands-suggestion";
export * from "@/plane-editor/types";
export * from "./document-collaborative-events";
@@ -1 +0,0 @@
export * from "src/ce/extensions";
-1
View File
@@ -1 +0,0 @@
export * from "src/ce/types";
+7546 -9210
View File
File diff suppressed because it is too large Load Diff