mirror of
https://github.com/makeplane/plane.git
synced 2026-02-24 20:20:49 +01:00
refactor: core without props
This commit is contained in:
@@ -1,44 +0,0 @@
|
||||
// plane editor
|
||||
import {
|
||||
getAllDocumentFormatsFromDocumentEditorBinaryData,
|
||||
getAllDocumentFormatsFromRichTextEditorBinaryData,
|
||||
getBinaryDataFromDocumentEditorHTMLString,
|
||||
getBinaryDataFromRichTextEditorHTMLString,
|
||||
} from "@plane/editor";
|
||||
// plane types
|
||||
import { TDocumentPayload } from "@plane/types";
|
||||
|
||||
type TArgs = {
|
||||
document_html: string;
|
||||
variant: "rich" | "document";
|
||||
};
|
||||
|
||||
export const convertHTMLDocumentToAllFormats = (args: TArgs): TDocumentPayload => {
|
||||
const { document_html, variant } = args;
|
||||
|
||||
let allFormats: TDocumentPayload;
|
||||
|
||||
if (variant === "rich") {
|
||||
const contentBinary = getBinaryDataFromRichTextEditorHTMLString(document_html);
|
||||
const { contentBinaryEncoded, contentHTML, contentJSON } =
|
||||
getAllDocumentFormatsFromRichTextEditorBinaryData(contentBinary);
|
||||
allFormats = {
|
||||
description: contentJSON,
|
||||
description_html: contentHTML,
|
||||
description_binary: contentBinaryEncoded,
|
||||
};
|
||||
} else if (variant === "document") {
|
||||
const contentBinary = getBinaryDataFromDocumentEditorHTMLString(document_html);
|
||||
const { contentBinaryEncoded, contentHTML, contentJSON } =
|
||||
getAllDocumentFormatsFromDocumentEditorBinaryData(contentBinary);
|
||||
allFormats = {
|
||||
description: contentJSON,
|
||||
description_html: contentHTML,
|
||||
description_binary: contentBinaryEncoded,
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Invalid variant provided: ${variant}`);
|
||||
}
|
||||
|
||||
return allFormats;
|
||||
};
|
||||
@@ -6,10 +6,10 @@ import helmet from "helmet";
|
||||
// hocuspocus server
|
||||
import { getHocusPocusServer } from "@/core/hocuspocus-server.js";
|
||||
// helpers
|
||||
import { convertHTMLDocumentToAllFormats } from "@/core/helpers/convert-document.js";
|
||||
import { logger, manualLogger } from "@/core/helpers/logger.js";
|
||||
// types
|
||||
import { TConvertDocumentRequestBody } from "@/core/types/common.js";
|
||||
import { getAllDocumentFormatsFromHTMLString } from "@plane/editor/lib";
|
||||
|
||||
export class Server {
|
||||
private app: any;
|
||||
@@ -72,9 +72,8 @@ export class Server {
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { description, description_binary } = convertHTMLDocumentToAllFormats({
|
||||
const { description, description_binary } = getAllDocumentFormatsFromHTMLString({
|
||||
document_html: description_html,
|
||||
variant,
|
||||
});
|
||||
res.status(200).json({
|
||||
description,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useEffect } from "react";
|
||||
// plane editor
|
||||
import { EditorRefApi, getBinaryDataFromDocumentEditorHTMLString } from "@plane/editor";
|
||||
import { EditorRefApi, getBinaryDataFromHTMLString } from "@plane/editor";
|
||||
// plane types
|
||||
import { TDocumentPayload } from "@plane/types";
|
||||
// hooks
|
||||
@@ -27,7 +27,7 @@ export const usePageFallback = (args: TArgs) => {
|
||||
if (latestEncodedDescription && latestEncodedDescription.byteLength > 0) {
|
||||
latestDecodedDescription = new Uint8Array(latestEncodedDescription);
|
||||
} else {
|
||||
latestDecodedDescription = getBinaryDataFromDocumentEditorHTMLString("<p></p>");
|
||||
latestDecodedDescription = getBinaryDataFromHTMLString("<p></p>");
|
||||
}
|
||||
|
||||
editor.setProviderDocument(latestDecodedDescription);
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { Extensions } from "@tiptap/core";
|
||||
|
||||
export const CoreEditorAdditionalExtensionsWithoutProps: Extensions = [];
|
||||
3
packages/editor/src/ce/extensions/parser-kit.ts
Normal file
3
packages/editor/src/ce/extensions/parser-kit.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Extensions } from "@tiptap/core";
|
||||
|
||||
export const PARSER_KIT_ADDITIONAL_EXTENSIONS: Extensions = [];
|
||||
@@ -1,13 +1,10 @@
|
||||
import type { Extensions } from "@tiptap/core";
|
||||
import React, { useMemo } from "react";
|
||||
import React 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
|
||||
@@ -21,7 +18,7 @@ const CollaborativeDocumentEditor: React.FC<ICollaborativeDocumentEditorProps> =
|
||||
bubbleMenuEnabled = true,
|
||||
containerClassName,
|
||||
documentLoaderClassName,
|
||||
extensions: externalExtensions = [],
|
||||
extensions,
|
||||
disabledExtensions,
|
||||
displayConfig = DEFAULT_DISPLAY_CONFIG,
|
||||
editable,
|
||||
@@ -47,20 +44,6 @@ 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,
|
||||
|
||||
@@ -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
|
||||
@@ -39,13 +39,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"),
|
||||
|
||||
@@ -8,8 +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";
|
||||
export * from "./extensions";
|
||||
|
||||
@@ -6,7 +6,7 @@ import StarterKit from "@tiptap/starter-kit";
|
||||
// helpers
|
||||
import { isValidHttpUrl } from "@/helpers/common";
|
||||
// plane editor imports
|
||||
import { CoreEditorAdditionalExtensionsWithoutProps } from "@/plane-editor/extensions/core/without-props";
|
||||
import { PARSER_KIT_ADDITIONAL_EXTENSIONS } from "@/plane-editor/extensions/parser-kit";
|
||||
// extensions
|
||||
import { CustomCalloutExtensionConfig } from "./callout/extension-config";
|
||||
import { CustomCodeBlockExtensionWithoutProps } from "./code/without-props";
|
||||
@@ -21,9 +21,8 @@ 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 = [
|
||||
export const PARSER_KIT = [
|
||||
StarterKit.configure({
|
||||
bulletList: {
|
||||
HTMLAttributes: {
|
||||
@@ -99,7 +98,5 @@ export const CoreEditorExtensionsWithoutProps = [
|
||||
CustomTextAlignExtension,
|
||||
CustomCalloutExtensionConfig,
|
||||
CustomColorExtension,
|
||||
...CoreEditorAdditionalExtensionsWithoutProps,
|
||||
...PARSER_KIT_ADDITIONAL_EXTENSIONS,
|
||||
];
|
||||
|
||||
export const DocumentEditorExtensionsWithoutProps = [WorkItemEmbedExtensionConfig];
|
||||
@@ -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";
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Editor, Range } from "@tiptap/core";
|
||||
import type { Level } from "@tiptap/extension-heading";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
@@ -12,7 +13,7 @@ export const setText = (editor: Editor, range?: Range) => {
|
||||
else editor.chain().focus().setNode(CORE_EXTENSIONS.PARAGRAPH).run();
|
||||
};
|
||||
|
||||
export const toggleHeading = (editor: Editor, level: 1 | 2 | 3 | 4 | 5 | 6, range?: Range) => {
|
||||
export const toggleHeading = (editor: Editor, level: Level, range?: Range) => {
|
||||
if (range) editor.chain().focus().deleteRange(range).setNode(CORE_EXTENSIONS.HEADING, { level }).run();
|
||||
else editor.chain().focus().toggleHeading({ level }).run();
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
replaceAdditionalAssetsInHTMLContent,
|
||||
} from "@/plane-editor/helpers/parser";
|
||||
// local imports
|
||||
import { convertHTMLDocumentToAllFormats } from "./yjs-utils";
|
||||
import { getAllDocumentFormatsFromHTMLString } from "./yjs-utils";
|
||||
|
||||
/**
|
||||
* @description function to extract all assets from HTML content
|
||||
@@ -62,10 +62,9 @@ export const getEditorContentWithReplacedAssets = async (props: {
|
||||
entityId: string;
|
||||
entityType: TEditorAssetType;
|
||||
projectId: string | undefined;
|
||||
variant: "rich" | "document";
|
||||
duplicateAssetService: (params: TDuplicateAssetData) => Promise<TDuplicateAssetResponse>;
|
||||
}): Promise<TDocumentPayload> => {
|
||||
const { descriptionHTML, entityId, entityType, projectId, variant, duplicateAssetService } = props;
|
||||
const { descriptionHTML, entityId, entityType, projectId, duplicateAssetService } = props;
|
||||
let replacedDescription = descriptionHTML;
|
||||
// step 1: extract image assets from the description
|
||||
const assetIds = extractAssetsFromHTMLContent(descriptionHTML);
|
||||
@@ -86,9 +85,8 @@ export const getEditorContentWithReplacedAssets = async (props: {
|
||||
}
|
||||
}
|
||||
// step 4: convert the description to the document payload
|
||||
const documentPayload = convertHTMLDocumentToAllFormats({
|
||||
const documentPayload = getAllDocumentFormatsFromHTMLString({
|
||||
document_html: replacedDescription,
|
||||
variant,
|
||||
});
|
||||
return documentPayload;
|
||||
};
|
||||
|
||||
@@ -4,17 +4,10 @@ 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 { PARSER_KIT } from "@/extensions/parser-kit";
|
||||
|
||||
// editor extension configs
|
||||
const RICH_TEXT_EDITOR_EXTENSIONS = CoreEditorExtensionsWithoutProps;
|
||||
const DOCUMENT_EDITOR_EXTENSIONS = [...CoreEditorExtensionsWithoutProps, ...DocumentEditorExtensionsWithoutProps];
|
||||
// editor schemas
|
||||
const richTextEditorSchema = getSchema(RICH_TEXT_EDITOR_EXTENSIONS);
|
||||
const documentEditorSchema = getSchema(DOCUMENT_EDITOR_EXTENSIONS);
|
||||
// parser schema
|
||||
const parserSchema = getSchema(PARSER_KIT);
|
||||
|
||||
/**
|
||||
* @description apply updates to a doc and return the updated doc in binary format
|
||||
@@ -53,26 +46,11 @@ export const convertBase64StringToBinaryData = (document: string): ArrayBuffer =
|
||||
* @param {string} descriptionHTML
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export const getBinaryDataFromRichTextEditorHTMLString = (descriptionHTML: string): Uint8Array => {
|
||||
export const getBinaryDataFromHTMLString = (descriptionHTML: string): Uint8Array => {
|
||||
// convert HTML to JSON
|
||||
const contentJSON = generateJSON(descriptionHTML ?? "<p></p>", RICH_TEXT_EDITOR_EXTENSIONS);
|
||||
const contentJSON = generateJSON(descriptionHTML ?? "<p></p>", PARSER_KIT);
|
||||
// convert JSON to Y.Doc format
|
||||
const transformedData = prosemirrorJSONToYDoc(richTextEditorSchema, contentJSON, "default");
|
||||
// convert Y.Doc to Uint8Array format
|
||||
const encodedData = Y.encodeStateAsUpdate(transformedData);
|
||||
return encodedData;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description this function generates the binary equivalent of html content for the document editor
|
||||
* @param {string} descriptionHTML
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export const getBinaryDataFromDocumentEditorHTMLString = (descriptionHTML: string): Uint8Array => {
|
||||
// convert HTML to JSON
|
||||
const contentJSON = generateJSON(descriptionHTML ?? "<p></p>", DOCUMENT_EDITOR_EXTENSIONS);
|
||||
// convert JSON to Y.Doc format
|
||||
const transformedData = prosemirrorJSONToYDoc(documentEditorSchema, contentJSON, "default");
|
||||
const transformedData = prosemirrorJSONToYDoc(parserSchema, contentJSON, "default");
|
||||
// convert Y.Doc to Uint8Array format
|
||||
const encodedData = Y.encodeStateAsUpdate(transformedData);
|
||||
return encodedData;
|
||||
@@ -83,7 +61,7 @@ export const getBinaryDataFromDocumentEditorHTMLString = (descriptionHTML: strin
|
||||
* @param {Uint8Array} description
|
||||
* @returns
|
||||
*/
|
||||
export const getAllDocumentFormatsFromRichTextEditorBinaryData = (
|
||||
export const getAllDocumentFormatsFromBinaryData = (
|
||||
description: Uint8Array
|
||||
): {
|
||||
contentBinaryEncoded: string;
|
||||
@@ -96,38 +74,9 @@ export const getAllDocumentFormatsFromRichTextEditorBinaryData = (
|
||||
Y.applyUpdate(yDoc, description);
|
||||
// convert to JSON
|
||||
const type = yDoc.getXmlFragment("default");
|
||||
const contentJSON = yXmlFragmentToProseMirrorRootNode(type, richTextEditorSchema).toJSON();
|
||||
const contentJSON = yXmlFragmentToProseMirrorRootNode(type, parserSchema).toJSON();
|
||||
// convert to HTML
|
||||
const contentHTML = generateHTML(contentJSON, RICH_TEXT_EDITOR_EXTENSIONS);
|
||||
|
||||
return {
|
||||
contentBinaryEncoded: base64Data,
|
||||
contentJSON,
|
||||
contentHTML,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @description this function generates all document formats for the provided binary data for the document editor
|
||||
* @param {Uint8Array} description
|
||||
* @returns
|
||||
*/
|
||||
export const getAllDocumentFormatsFromDocumentEditorBinaryData = (
|
||||
description: Uint8Array
|
||||
): {
|
||||
contentBinaryEncoded: string;
|
||||
contentJSON: object;
|
||||
contentHTML: string;
|
||||
} => {
|
||||
// encode binary description data
|
||||
const base64Data = convertBinaryDataToBase64String(description);
|
||||
const yDoc = new Y.Doc();
|
||||
Y.applyUpdate(yDoc, description);
|
||||
// convert to JSON
|
||||
const type = yDoc.getXmlFragment("default");
|
||||
const contentJSON = yXmlFragmentToProseMirrorRootNode(type, documentEditorSchema).toJSON();
|
||||
// convert to HTML
|
||||
const contentHTML = generateHTML(contentJSON, DOCUMENT_EDITOR_EXTENSIONS);
|
||||
const contentHTML = generateHTML(contentJSON, PARSER_KIT);
|
||||
|
||||
return {
|
||||
contentBinaryEncoded: base64Data,
|
||||
@@ -138,47 +87,24 @@ export const getAllDocumentFormatsFromDocumentEditorBinaryData = (
|
||||
|
||||
type TConvertHTMLDocumentToAllFormatsArgs = {
|
||||
document_html: string;
|
||||
variant: "rich" | "document";
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Converts HTML content to all supported document formats (JSON, HTML, and binary)
|
||||
* @description Converts HTML string to all supported document formats (JSON, HTML, and binary)
|
||||
* @param {TConvertHTMLDocumentToAllFormatsArgs} args - Arguments containing HTML content and variant type
|
||||
* @param {string} args.document_html - The HTML content to convert
|
||||
* @param {"rich" | "document"} args.variant - The type of editor variant to use for conversion
|
||||
* @returns {TDocumentPayload} Object containing the document in all supported formats
|
||||
* @throws {Error} If an invalid variant is provided
|
||||
*/
|
||||
export const convertHTMLDocumentToAllFormats = (args: TConvertHTMLDocumentToAllFormatsArgs): TDocumentPayload => {
|
||||
const { document_html, variant } = args;
|
||||
export const getAllDocumentFormatsFromHTMLString = (args: TConvertHTMLDocumentToAllFormatsArgs): TDocumentPayload => {
|
||||
const { document_html } = args;
|
||||
|
||||
let allFormats: TDocumentPayload;
|
||||
// Convert HTML to binary format for rich text editor
|
||||
const contentBinary = getBinaryDataFromHTMLString(document_html);
|
||||
// Generate all document formats from the binary data
|
||||
const { contentBinaryEncoded, contentHTML, contentJSON } = getAllDocumentFormatsFromBinaryData(contentBinary);
|
||||
|
||||
if (variant === "rich") {
|
||||
// Convert HTML to binary format for rich text editor
|
||||
const contentBinary = getBinaryDataFromRichTextEditorHTMLString(document_html);
|
||||
// Generate all document formats from the binary data
|
||||
const { contentBinaryEncoded, contentHTML, contentJSON } =
|
||||
getAllDocumentFormatsFromRichTextEditorBinaryData(contentBinary);
|
||||
allFormats = {
|
||||
description: contentJSON,
|
||||
description_html: contentHTML,
|
||||
description_binary: contentBinaryEncoded,
|
||||
};
|
||||
} else if (variant === "document") {
|
||||
// Convert HTML to binary format for document editor
|
||||
const contentBinary = getBinaryDataFromDocumentEditorHTMLString(document_html);
|
||||
// Generate all document formats from the binary data
|
||||
const { contentBinaryEncoded, contentHTML, contentJSON } =
|
||||
getAllDocumentFormatsFromDocumentEditorBinaryData(contentBinary);
|
||||
allFormats = {
|
||||
description: contentJSON,
|
||||
description_html: contentHTML,
|
||||
description_binary: contentBinaryEncoded,
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Invalid variant provided: ${variant}`);
|
||||
}
|
||||
|
||||
return allFormats;
|
||||
return {
|
||||
description: contentJSON,
|
||||
description_html: contentHTML,
|
||||
description_binary: contentBinaryEncoded,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from "@/extensions/core-without-props";
|
||||
export * from "@/extensions/parser-kit";
|
||||
export * from "@/constants/document-collaborative-events";
|
||||
export * from "@/helpers/get-document-server-event";
|
||||
export * from "@/helpers/yjs-utils";
|
||||
|
||||
Reference in New Issue
Block a user