Merge branch 'preview' into devin/1734544044-refactor-live-server

This commit is contained in:
Palanikannan M
2025-03-19 17:43:10 +05:30
13 changed files with 114 additions and 39 deletions

View File

@@ -130,7 +130,7 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
CustomCodeInlineExtension,
Markdown.configure({
html: true,
transformCopiedText: true,
transformCopiedText: false,
transformPastedText: true,
breaks: true,
}),

View File

@@ -1,12 +1,15 @@
import { mergeAttributes } from "@tiptap/core";
import Mention, { MentionOptions } from "@tiptap/extension-mention";
import { MarkdownSerializerState } from "@tiptap/pm/markdown";
import { Node as NodeType } from "@tiptap/pm/model";
// types
import { TMentionHandler } from "@/types";
// local types
import { EMentionComponentAttributeNames } from "./types";
import { EMentionComponentAttributeNames, TMentionComponentAttributes } from "./types";
export type TMentionExtensionOptions = MentionOptions & {
renderComponent: TMentionHandler["renderComponent"];
getMentionedEntityDetails: TMentionHandler["getMentionedEntityDetails"];
};
export const CustomMentionExtensionConfig = Mention.extend<TMentionExtensionOptions>({
@@ -40,9 +43,26 @@ export const CustomMentionExtensionConfig = Mention.extend<TMentionExtensionOpti
class: "mention",
},
addStorage(this) {
renderText({ node }) {
return getMentionDisplayText(this.options, node);
},
addStorage() {
const options = this.options;
return {
mentionsOpen: false,
markdown: {
serialize(state: MarkdownSerializerState, node: NodeType) {
state.write(getMentionDisplayText(options, node));
},
},
};
},
});
function getMentionDisplayText(options: TMentionExtensionOptions, node: NodeType): string {
const attrs = node.attrs as TMentionComponentAttributes;
const mentionEntityId = attrs[EMentionComponentAttributeNames.ENTITY_IDENTIFIER];
const mentionEntityDetails = options.getMentionedEntityDetails?.(mentionEntityId);
return `@${mentionEntityDetails?.display_name ?? attrs[EMentionComponentAttributeNames.ID] ?? mentionEntityId}`;
}

View File

@@ -9,12 +9,13 @@ import { MentionNodeView } from "./mention-node-view";
import { renderMentionsDropdown } from "./utils";
export const CustomMentionExtension = (props: TMentionHandler) => {
const { searchCallback, renderComponent } = props;
const { searchCallback, renderComponent, getMentionedEntityDetails } = props;
return CustomMentionExtensionConfig.extend({
addOptions(this) {
return {
...this.parent?.(),
renderComponent,
getMentionedEntityDetails,
};
},

View File

@@ -1,5 +1,5 @@
// plane types
import { TSearchEntities } from "@plane/types";
import { IUserLite, TSearchEntities } from "@plane/types";
export type TMentionSuggestion = {
entity_identifier: string;
@@ -20,6 +20,7 @@ export type TMentionComponentProps = Pick<TMentionSuggestion, "entity_identifier
export type TReadOnlyMentionHandler = {
renderComponent: (props: TMentionComponentProps) => React.ReactNode;
getMentionedEntityDetails?: (entity_identifier: string) => { display_name: string } | undefined;
};
export type TMentionHandler = TReadOnlyMentionHandler & {

View File

@@ -7,6 +7,8 @@ import { EditorMentionsRoot } from "@/components/editor";
// helpers
import { cn } from "@/helpers/common.helper";
import { getReadOnlyEditorFileHandlers } from "@/helpers/editor.helper";
// store hooks
import { useMember } from "@/hooks/store";
type LiteTextReadOnlyEditorWrapperProps = MakeOptional<
Omit<ILiteTextReadOnlyEditor, "fileHandler" | "mentionHandler">,
@@ -17,22 +19,29 @@ type LiteTextReadOnlyEditorWrapperProps = MakeOptional<
};
export const LiteTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, LiteTextReadOnlyEditorWrapperProps>(
({ anchor, workspaceId, disabledExtensions, ...props }, ref) => (
<LiteTextReadOnlyEditorWithRef
ref={ref}
disabledExtensions={disabledExtensions ?? []}
fileHandler={getReadOnlyEditorFileHandlers({
anchor,
workspaceId,
})}
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
}}
{...props}
// overriding the customClassName to add relative class passed
containerClassName={cn(props.containerClassName, "relative p-2")}
/>
)
({ anchor, workspaceId, disabledExtensions, ...props }, ref) => {
const { getMemberById } = useMember();
return (
<LiteTextReadOnlyEditorWithRef
ref={ref}
disabledExtensions={disabledExtensions ?? []}
fileHandler={getReadOnlyEditorFileHandlers({
anchor,
workspaceId,
})}
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({
display_name: getMemberById(id)?.member__display_name ?? "",
}),
}}
{...props}
// overriding the customClassName to add relative class passed
containerClassName={cn(props.containerClassName, "relative p-2")}
/>
);
}
);
LiteTextReadOnlyEditor.displayName = "LiteTextReadOnlyEditor";

View File

@@ -6,6 +6,8 @@ import { MakeOptional } from "@plane/types";
import { EditorMentionsRoot } from "@/components/editor";
// helpers
import { getEditorFileHandlers } from "@/helpers/editor.helper";
// store hooks
import { useMember } from "@/hooks/store";
interface RichTextEditorWrapperProps
extends MakeOptional<Omit<IRichTextEditor, "fileHandler" | "mentionHandler">, "disabledExtensions"> {
@@ -16,11 +18,14 @@ interface RichTextEditorWrapperProps
export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProps>((props, ref) => {
const { anchor, containerClassName, uploadFile, workspaceId, disabledExtensions, ...rest } = props;
const { getMemberById } = useMember();
return (
<RichTextEditorWithRef
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({
display_name: getMemberById(id)?.member__display_name ?? "",
}),
}}
ref={ref}
disabledExtensions={disabledExtensions ?? []}

View File

@@ -7,6 +7,8 @@ import { EditorMentionsRoot } from "@/components/editor";
// helpers
import { cn } from "@/helpers/common.helper";
import { getReadOnlyEditorFileHandlers } from "@/helpers/editor.helper";
// store hooks
import { useMember } from "@/hooks/store";
type RichTextReadOnlyEditorWrapperProps = MakeOptional<
Omit<IRichTextReadOnlyEditor, "fileHandler" | "mentionHandler">,
@@ -17,22 +19,29 @@ type RichTextReadOnlyEditorWrapperProps = MakeOptional<
};
export const RichTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, RichTextReadOnlyEditorWrapperProps>(
({ anchor, workspaceId, disabledExtensions, ...props }, ref) => (
<RichTextReadOnlyEditorWithRef
ref={ref}
disabledExtensions={disabledExtensions ?? []}
fileHandler={getReadOnlyEditorFileHandlers({
anchor,
workspaceId,
})}
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
}}
{...props}
// overriding the customClassName to add relative class passed
containerClassName={cn("relative p-0 border-none", props.containerClassName)}
/>
)
({ anchor, workspaceId, disabledExtensions, ...props }, ref) => {
const { getMemberById } = useMember();
return (
<RichTextReadOnlyEditorWithRef
ref={ref}
disabledExtensions={disabledExtensions ?? []}
fileHandler={getReadOnlyEditorFileHandlers({
anchor,
workspaceId,
})}
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({
display_name: getMemberById(id)?.member__display_name ?? "",
}),
}}
{...props}
// overriding the customClassName to add relative class passed
containerClassName={cn("relative p-0 border-none", props.containerClassName)}
/>
);
}
);
RichTextReadOnlyEditor.displayName = "RichTextReadOnlyEditor";

View File

@@ -13,6 +13,8 @@ import { cn } from "@/helpers/common.helper";
import { isCommentEmpty } from "@/helpers/string.helper";
// hooks
import { useEditorConfig, useEditorMention } from "@/hooks/editor";
// store hooks
import { useMember } from "@/hooks/store";
// plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
// plane web services
@@ -57,6 +59,8 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
const [isFocused, setIsFocused] = useState(showToolbarInitially);
// editor flaggings
const { liteTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString());
// store hooks
const { getUserDetails } = useMember();
// use editor mention
const { fetchMentions } = useEditorMention({
searchEntity: async (payload) =>
@@ -97,6 +101,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
return res;
},
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
}}
placeholder={placeholder}
containerClassName={cn(containerClassName, "relative")}

View File

@@ -8,6 +8,8 @@ import { EditorMentionsRoot } from "@/components/editor";
import { cn } from "@/helpers/common.helper";
// hooks
import { useEditorConfig } from "@/hooks/editor";
// store hooks
import { useMember } from "@/hooks/store";
// plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
@@ -22,6 +24,9 @@ type LiteTextReadOnlyEditorWrapperProps = MakeOptional<
export const LiteTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, LiteTextReadOnlyEditorWrapperProps>(
({ workspaceId, workspaceSlug, projectId, disabledExtensions: additionalDisabledExtensions, ...props }, ref) => {
// store hooks
const { getUserDetails } = useMember();
// editor flaggings
const { liteTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString());
// editor config
@@ -38,6 +43,7 @@ export const LiteTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, Lit
})}
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
}}
{...props}
// overriding the containerClassName to add relative class passed

View File

@@ -8,6 +8,8 @@ import { EditorMentionsRoot } from "@/components/editor";
import { cn } from "@/helpers/common.helper";
// hooks
import { useEditorConfig, useEditorMention } from "@/hooks/editor";
// store hooks
import { useMember } from "@/hooks/store";
// plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
@@ -31,6 +33,8 @@ export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProp
disabledExtensions: additionalDisabledExtensions,
...rest
} = props;
// store hooks
const { getUserDetails } = useMember();
// editor flaggings
const { richTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString());
// use editor mention
@@ -57,6 +61,7 @@ export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProp
return res;
},
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
}}
{...rest}
containerClassName={cn("relative pl-3 pb-3", containerClassName)}

View File

@@ -8,6 +8,8 @@ import { EditorMentionsRoot } from "@/components/editor";
import { cn } from "@/helpers/common.helper";
// hooks
import { useEditorConfig } from "@/hooks/editor";
// store hooks
import { useMember } from "@/hooks/store";
// plane web hooks
import { useEditorFlagging } from "@/plane-web/hooks/use-editor-flagging";
@@ -22,6 +24,9 @@ type RichTextReadOnlyEditorWrapperProps = MakeOptional<
export const RichTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, RichTextReadOnlyEditorWrapperProps>(
({ workspaceId, workspaceSlug, projectId, disabledExtensions: additionalDisabledExtensions, ...props }, ref) => {
// store hooks
const { getUserDetails } = useMember();
// editor flaggings
const { richTextEditor: disabledExtensions } = useEditorFlagging(workspaceSlug?.toString());
// editor config
@@ -38,6 +43,7 @@ export const RichTextReadOnlyEditor = React.forwardRef<EditorReadOnlyRefApi, Ric
})}
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
}}
{...props}
// overriding the containerClassName to add relative class passed

View File

@@ -22,7 +22,7 @@ import { cn, LIVE_BASE_PATH, LIVE_BASE_URL } from "@/helpers/common.helper";
import { generateRandomColor } from "@/helpers/string.helper";
// hooks
import { useEditorMention } from "@/hooks/editor";
import { useUser, useWorkspace } from "@/hooks/store";
import { useUser, useWorkspace, useMember } from "@/hooks/store";
import { usePageFilters } from "@/hooks/use-page-filters";
// plane web components
import { EditorAIMenu } from "@/plane-web/components/pages";
@@ -68,6 +68,8 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
// store hooks
const { data: currentUser } = useUser();
const { getWorkspaceBySlug } = useWorkspace();
const { getUserDetails } = useMember();
// derived values
const { id: pageId, name: pageTitle, isContentEditable, updateTitle } = page;
const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id ?? "";
@@ -192,6 +194,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
return res;
},
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
}}
embedHandler={{
issue: issueEmbedProps,

View File

@@ -12,6 +12,8 @@ import { EditorMentionsRoot } from "@/components/editor";
import { useEditorConfig } from "@/hooks/editor";
import { useWorkspace } from "@/hooks/store";
import { usePageFilters } from "@/hooks/use-page-filters";
// store hooks
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";
@@ -25,6 +27,8 @@ export type TVersionEditorProps = {
export const PagesVersionEditor: React.FC<TVersionEditorProps> = observer((props) => {
const { activeVersion, currentVersionDescription, isCurrentVersionActive, versionDetails } = props;
// store hooks
const { getUserDetails } = useMember();
// params
const { workspaceSlug, projectId } = useParams();
// store hooks
@@ -108,6 +112,7 @@ export const PagesVersionEditor: React.FC<TVersionEditorProps> = observer((props
})}
mentionHandler={{
renderComponent: (props) => <EditorMentionsRoot {...props} />,
getMentionedEntityDetails: (id: string) => ({ display_name: getUserDetails(id)?.display_name ?? "" }),
}}
embedHandler={{
issue: {