mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: pages collaboration
This commit is contained in:
parent
6f190ea6ee
commit
17c539658b
@ -430,17 +430,17 @@ class PagesDescriptionViewSet(BaseViewSet):
|
||||
if page.description_yjs:
|
||||
Y.apply_update(existing_doc, page.description_yjs)
|
||||
|
||||
# Load the new data into a separate YDoc
|
||||
new_doc = Y.YDoc()
|
||||
Y.apply_update(new_doc, new_binary_data)
|
||||
# # Load the new data into a separate YDoc
|
||||
# new_doc = Y.YDoc()
|
||||
Y.apply_update(existing_doc, new_binary_data)
|
||||
|
||||
# Merge the new data into the existing data
|
||||
# This will automatically resolve any conflicts
|
||||
new_state_vector = Y.encode_state_vector(new_doc)
|
||||
diff = Y.encode_state_as_update(existing_doc, new_state_vector)
|
||||
Y.apply_update(existing_doc, diff)
|
||||
# # Merge the new data into the existing data
|
||||
# # This will automatically resolve any conflicts
|
||||
# new_state_vector = Y.encode_state_vector(new_doc)
|
||||
# diff = Y.encode_state_as_update(existing_doc, new_state_vector)
|
||||
# Y.apply_update(existing_doc, diff)
|
||||
|
||||
# Encode the updated state as binary data
|
||||
# # Encode the updated state as binary data
|
||||
updated_binary_data = Y.encode_state_as_update(existing_doc)
|
||||
|
||||
# Store the updated binary data
|
||||
|
@ -12,18 +12,17 @@ import { insertContentAtSavedSelection } from "src/helpers/insert-content-at-cur
|
||||
import { EditorMenuItemNames, getEditorMenuItems } from "src/ui/menus/menu-items";
|
||||
import { EditorRefApi } from "src/types/editor-ref-api";
|
||||
import { IMarking, scrollSummary } from "src/helpers/scroll-to-node";
|
||||
|
||||
interface CustomEditorProps {
|
||||
export interface CustomEditorProps {
|
||||
id?: string;
|
||||
uploadFile: UploadImage;
|
||||
restoreFile: RestoreImage;
|
||||
deleteFile: DeleteImage;
|
||||
cancelUploadImage?: () => void;
|
||||
initialValue: string;
|
||||
initialValue?: string;
|
||||
editorClassName: string;
|
||||
// undefined when prop is not passed, null if intentionally passed to stop
|
||||
// swr syncing
|
||||
value: string | null | undefined;
|
||||
value?: string | null | undefined;
|
||||
onChange?: (json: object, html: string) => void;
|
||||
extensions?: any;
|
||||
editorProps?: EditorProps;
|
||||
|
@ -24,6 +24,7 @@ export * from "src/ui/menus/menu-items";
|
||||
export * from "src/lib/editor-commands";
|
||||
|
||||
// types
|
||||
export type { CustomEditorProps } from "src/hooks/use-editor";
|
||||
export type { DeleteImage } from "src/types/delete-image";
|
||||
export type { UploadImage } from "src/types/upload-image";
|
||||
export type { EditorRefApi, EditorReadOnlyRefApi } from "src/types/editor-ref-api";
|
||||
|
@ -34,12 +34,17 @@
|
||||
"@plane/ui": "*",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@tiptap/core": "^2.1.13",
|
||||
"@tiptap/extension-collaboration": "^2.3.2",
|
||||
"@tiptap/pm": "^2.1.13",
|
||||
"@tiptap/suggestion": "^2.1.13",
|
||||
"lucide-react": "^0.378.0",
|
||||
"react-popper": "^2.3.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"uuid": "^9.0.1"
|
||||
"uuid": "^9.0.1",
|
||||
"y-indexeddb": "^9.0.12",
|
||||
"y-prosemirror": "^1.2.5",
|
||||
"y-protocols": "^1.0.6",
|
||||
"yjs": "^13.6.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.15.3",
|
||||
|
101
packages/editor/document-editor/src/hooks/use-document-editor.ts
Normal file
101
packages/editor/document-editor/src/hooks/use-document-editor.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import { useLayoutEffect, useMemo } from "react";
|
||||
import {
|
||||
DeleteImage,
|
||||
EditorRefApi,
|
||||
IMentionHighlight,
|
||||
IMentionSuggestion,
|
||||
RestoreImage,
|
||||
UploadImage,
|
||||
useEditor,
|
||||
} from "@plane/editor-core";
|
||||
import * as Y from "yjs";
|
||||
import { CollaborationProvider } from "src/providers/collaboration-provider";
|
||||
import { DocumentEditorExtensions } from "src/ui/extensions";
|
||||
import { IndexeddbPersistence } from "y-indexeddb";
|
||||
import { EditorProps } from "@tiptap/pm/view";
|
||||
|
||||
type DocumentEditorProps = {
|
||||
id?: string;
|
||||
uploadFile: UploadImage;
|
||||
restoreFile: RestoreImage;
|
||||
deleteFile: DeleteImage;
|
||||
cancelUploadImage?: () => void;
|
||||
value: Uint8Array;
|
||||
editorClassName: string;
|
||||
onChange: (binaryString: string, html: string) => void;
|
||||
extensions?: any;
|
||||
editorProps?: EditorProps;
|
||||
forwardedRef?: React.MutableRefObject<EditorRefApi | null>;
|
||||
mentionHandler: {
|
||||
highlights: () => Promise<IMentionHighlight[]>;
|
||||
suggestions?: () => Promise<IMentionSuggestion[]>;
|
||||
};
|
||||
handleEditorReady?: (value: boolean) => void;
|
||||
placeholder?: string | ((isFocused: boolean, value: string) => string);
|
||||
setHideDragHandleFunction: (hideDragHandlerFromDragDrop: () => void) => void;
|
||||
tabIndex?: number;
|
||||
};
|
||||
|
||||
export const useDocumentEditor = ({
|
||||
uploadFile,
|
||||
id = "",
|
||||
deleteFile,
|
||||
cancelUploadImage,
|
||||
editorProps = {},
|
||||
value,
|
||||
editorClassName,
|
||||
onChange,
|
||||
forwardedRef,
|
||||
tabIndex,
|
||||
restoreFile,
|
||||
handleEditorReady,
|
||||
mentionHandler,
|
||||
placeholder,
|
||||
setHideDragHandleFunction,
|
||||
}: DocumentEditorProps) => {
|
||||
const provider = useMemo(
|
||||
() =>
|
||||
new CollaborationProvider({
|
||||
name: id,
|
||||
onChange,
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[id]
|
||||
);
|
||||
|
||||
const yDoc = useMemo(() => {
|
||||
if (value.byteLength !== 0) Y.applyUpdate(provider.document, value);
|
||||
return provider.document;
|
||||
}, [value, provider.document]);
|
||||
console.log("yDoc", yDoc);
|
||||
|
||||
// indexedDB provider
|
||||
useLayoutEffect(() => {
|
||||
const localProvider = new IndexeddbPersistence(id, provider.document);
|
||||
return () => {
|
||||
localProvider?.destroy();
|
||||
};
|
||||
}, [provider, id]);
|
||||
|
||||
const editor = useEditor({
|
||||
id,
|
||||
editorProps,
|
||||
editorClassName,
|
||||
restoreFile,
|
||||
uploadFile,
|
||||
deleteFile,
|
||||
cancelUploadImage,
|
||||
handleEditorReady,
|
||||
forwardedRef,
|
||||
mentionHandler,
|
||||
extensions: DocumentEditorExtensions({
|
||||
uploadFile,
|
||||
setHideDragHandle: setHideDragHandleFunction,
|
||||
provider,
|
||||
}),
|
||||
placeholder,
|
||||
tabIndex,
|
||||
});
|
||||
|
||||
return editor;
|
||||
};
|
@ -0,0 +1,70 @@
|
||||
import * as Y from "yjs";
|
||||
|
||||
export interface CompleteCollaboratorProviderConfiguration {
|
||||
/**
|
||||
* The identifier/name of your document
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The actual Y.js document
|
||||
*/
|
||||
document: Y.Doc;
|
||||
/**
|
||||
* onChange callback
|
||||
*/
|
||||
onChange: (binaryString: string, html: string) => void;
|
||||
}
|
||||
|
||||
export type CollaborationProviderConfiguration = Required<Pick<CompleteCollaboratorProviderConfiguration, "name">> &
|
||||
Partial<CompleteCollaboratorProviderConfiguration>;
|
||||
|
||||
export class CollaborationProvider {
|
||||
public configuration: CompleteCollaboratorProviderConfiguration = {
|
||||
name: "",
|
||||
// @ts-expect-error cannot be undefined
|
||||
document: undefined,
|
||||
onChange: () => {},
|
||||
};
|
||||
|
||||
intervals: any = {
|
||||
forceSync: null,
|
||||
};
|
||||
|
||||
timeoutId: any;
|
||||
|
||||
constructor(configuration: CollaborationProviderConfiguration) {
|
||||
this.setConfiguration(configuration);
|
||||
|
||||
this.timeoutId = null;
|
||||
|
||||
this.configuration.document = configuration.document ?? new Y.Doc();
|
||||
this.document.on("update", this.documentUpdateHandler.bind(this));
|
||||
}
|
||||
|
||||
public setConfiguration(configuration: Partial<CompleteCollaboratorProviderConfiguration> = {}): void {
|
||||
this.configuration = {
|
||||
...this.configuration,
|
||||
...configuration,
|
||||
};
|
||||
}
|
||||
|
||||
get document() {
|
||||
return this.configuration.document;
|
||||
}
|
||||
|
||||
documentUpdateHandler(update: Uint8Array, origin: any) {
|
||||
if (origin === this) return;
|
||||
|
||||
// debounce onChange call
|
||||
if (this.timeoutId !== null) clearTimeout(this.timeoutId);
|
||||
|
||||
this.timeoutId = setTimeout(() => {
|
||||
const docAsUint8Array = Y.encodeStateAsUpdate(this.document);
|
||||
const base64Doc = Buffer.from(docAsUint8Array).toString("base64");
|
||||
// const base64Doc = Buffer.from(update).toString("base64");
|
||||
|
||||
this.configuration.onChange?.(base64Doc, "<p></p>");
|
||||
this.timeoutId = null;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
@ -2,14 +2,20 @@ import { IssueWidgetPlaceholder } from "src/ui/extensions/widgets/issue-embed-wi
|
||||
|
||||
import { SlashCommand, DragAndDrop } from "@plane/editor-extensions";
|
||||
import { UploadImage } from "@plane/editor-core";
|
||||
import { CollaborationProvider } from "src/providers/collaboration-provider";
|
||||
import Collaboration from "@tiptap/extension-collaboration";
|
||||
|
||||
type TArguments = {
|
||||
uploadFile: UploadImage;
|
||||
setHideDragHandle?: (hideDragHandlerFromDragDrop: () => void) => void;
|
||||
provider: CollaborationProvider;
|
||||
};
|
||||
|
||||
export const DocumentEditorExtensions = ({ uploadFile, setHideDragHandle }: TArguments) => [
|
||||
export const DocumentEditorExtensions = ({ uploadFile, setHideDragHandle, provider }: TArguments) => [
|
||||
SlashCommand(uploadFile),
|
||||
DragAndDrop(setHideDragHandle),
|
||||
IssueWidgetPlaceholder(),
|
||||
Collaboration.configure({
|
||||
document: provider.document,
|
||||
}),
|
||||
];
|
||||
|
@ -4,17 +4,16 @@ import {
|
||||
DeleteImage,
|
||||
RestoreImage,
|
||||
getEditorClassNames,
|
||||
useEditor,
|
||||
EditorRefApi,
|
||||
IMentionHighlight,
|
||||
IMentionSuggestion,
|
||||
} from "@plane/editor-core";
|
||||
import { DocumentEditorExtensions } from "src/ui/extensions";
|
||||
import { PageRenderer } from "src/ui/components/page-renderer";
|
||||
import { useDocumentEditor } from "src/hooks/use-document-editor";
|
||||
|
||||
interface IDocumentEditor {
|
||||
initialValue: string;
|
||||
value?: string;
|
||||
id: string;
|
||||
value: Uint8Array;
|
||||
fileHandler: {
|
||||
cancel: () => void;
|
||||
delete: DeleteImage;
|
||||
@ -24,7 +23,7 @@ interface IDocumentEditor {
|
||||
handleEditorReady?: (value: boolean) => void;
|
||||
containerClassName?: string;
|
||||
editorClassName?: string;
|
||||
onChange: (json: object, html: string) => void;
|
||||
onChange: (binaryString: string, html: string) => void;
|
||||
forwardedRef?: React.MutableRefObject<EditorRefApi | null>;
|
||||
mentionHandler: {
|
||||
highlights: () => Promise<IMentionHighlight[]>;
|
||||
@ -37,8 +36,9 @@ interface IDocumentEditor {
|
||||
const DocumentEditor = (props: IDocumentEditor) => {
|
||||
const {
|
||||
onChange,
|
||||
initialValue,
|
||||
id,
|
||||
value,
|
||||
// value,
|
||||
fileHandler,
|
||||
containerClassName,
|
||||
editorClassName = "",
|
||||
@ -56,26 +56,22 @@ const DocumentEditor = (props: IDocumentEditor) => {
|
||||
const setHideDragHandleFunction = (hideDragHandlerFromDragDrop: () => void) => {
|
||||
setHideDragHandleOnMouseLeave(() => hideDragHandlerFromDragDrop);
|
||||
};
|
||||
// use editor
|
||||
const editor = useEditor({
|
||||
onChange(json, html) {
|
||||
onChange(json, html);
|
||||
},
|
||||
|
||||
// use document editor
|
||||
const editor = useDocumentEditor({
|
||||
id,
|
||||
editorClassName,
|
||||
restoreFile: fileHandler.restore,
|
||||
uploadFile: fileHandler.upload,
|
||||
deleteFile: fileHandler.delete,
|
||||
cancelUploadImage: fileHandler.cancel,
|
||||
initialValue,
|
||||
value,
|
||||
onChange,
|
||||
handleEditorReady,
|
||||
forwardedRef,
|
||||
mentionHandler,
|
||||
extensions: DocumentEditorExtensions({
|
||||
uploadFile: fileHandler.upload,
|
||||
setHideDragHandle: setHideDragHandleFunction,
|
||||
}),
|
||||
placeholder,
|
||||
setHideDragHandleFunction,
|
||||
tabIndex,
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useRouter } from "next/router";
|
||||
import { Control, Controller } from "react-hook-form";
|
||||
@ -22,6 +22,8 @@ import { usePageFilters } from "@/hooks/use-page-filters";
|
||||
import useReloadConfirmations from "@/hooks/use-reload-confirmation";
|
||||
// services
|
||||
import { FileService } from "@/services/file.service";
|
||||
import { PageService } from "@/services/page.service";
|
||||
const pageService = new PageService();
|
||||
// store
|
||||
import { IPageStore } from "@/store/pages/page.store";
|
||||
|
||||
@ -31,7 +33,6 @@ type Props = {
|
||||
control: Control<TPage, any>;
|
||||
editorRef: React.RefObject<EditorRefApi>;
|
||||
readOnlyEditorRef: React.RefObject<EditorReadOnlyRefApi>;
|
||||
swrPageDetails: TPage | undefined;
|
||||
handleSubmit: () => void;
|
||||
markings: IMarking[];
|
||||
pageStore: IPageStore;
|
||||
@ -49,12 +50,13 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
||||
editorRef,
|
||||
markings,
|
||||
readOnlyEditorRef,
|
||||
handleSubmit,
|
||||
// handleSubmit,
|
||||
pageStore,
|
||||
swrPageDetails,
|
||||
sidePeekVisible,
|
||||
updateMarkings,
|
||||
} = props;
|
||||
// states
|
||||
const [descriptionYJS, setDescriptionYJS] = useState<Uint8Array | null>(null);
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
@ -67,6 +69,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
||||
} = useMember();
|
||||
// derived values
|
||||
const workspaceId = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "" : "";
|
||||
const pageId = pageStore?.id ?? "";
|
||||
const pageTitle = pageStore?.name ?? "";
|
||||
const pageDescription = pageStore?.description_html;
|
||||
const { description_html, isContentEditable, updateTitle, isSubmitting, setIsSubmitting } = pageStore;
|
||||
@ -82,13 +85,70 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
||||
// page filters
|
||||
const { isFullWidth } = usePageFilters();
|
||||
|
||||
const { setShowAlert } = useReloadConfirmations(isSubmitting === "submitting");
|
||||
useReloadConfirmations(isSubmitting === "submitting");
|
||||
|
||||
// const { data: pageDescriptionYJS } = useSWR(
|
||||
// workspaceSlug && projectId && pageId ? `PAGE_DESCRIPTION_${workspaceSlug}_${projectId}_${pageId}` : null,
|
||||
// workspaceSlug && projectId && pageId
|
||||
// ? () => pageService.fetchDescriptionYJS(workspaceSlug.toString(), projectId.toString(), pageId.toString())
|
||||
// : null
|
||||
// );
|
||||
|
||||
const handleDescriptionChange = useCallback(
|
||||
(binaryString: string, descriptionHTML: string) => {
|
||||
if (!workspaceSlug || !projectId || !pageId) return;
|
||||
pageService.updateDescriptionYJS(workspaceSlug.toString(), projectId.toString(), pageId.toString(), {
|
||||
description_yjs: binaryString,
|
||||
description_html: descriptionHTML,
|
||||
});
|
||||
// setIsSubmitting("submitting");
|
||||
// setShowAlert(true);
|
||||
// onChange(description_html);
|
||||
// handleSubmit();
|
||||
},
|
||||
[pageId, projectId, workspaceSlug]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDescription = async () => {
|
||||
if (!workspaceSlug || !projectId || !pageId) return;
|
||||
console.log("fetching...");
|
||||
|
||||
const response = await fetch(
|
||||
`http://localhost:8000/api/workspaces/${workspaceSlug}/projects/${projectId}/pages/${pageId}/description/`,
|
||||
{
|
||||
credentials: "include",
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
}
|
||||
);
|
||||
const data = await response.arrayBuffer();
|
||||
setDescriptionYJS(new Uint8Array(data));
|
||||
// __AUTO_GENERATED_PRINT_VAR_START__
|
||||
console.log("fetchById data: %s", data); // __AUTO_GENERATED_PRINT_VAR_END__
|
||||
// if (data.byteLength === 0) {
|
||||
// const yjs = await fetchByIdIfExists(workspaceSlug, projectId, pageId);
|
||||
// if (yjs) {
|
||||
// console.log("not found in db:", yjs, yjs instanceof Uint8Array);
|
||||
// return yjs;
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
const interval = setInterval(() => {
|
||||
fetchDescription();
|
||||
}, 15000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [pageId, projectId, workspaceSlug]);
|
||||
|
||||
useEffect(() => {
|
||||
updateMarkings(description_html ?? "<p></p>");
|
||||
}, [description_html, updateMarkings]);
|
||||
|
||||
if (pageDescription === undefined) return <PageContentLoader />;
|
||||
if (pageDescription === undefined || pageId === undefined || !descriptionYJS) return <PageContentLoader />;
|
||||
|
||||
return (
|
||||
<div className="flex items-center h-full w-full overflow-y-auto">
|
||||
@ -125,8 +185,9 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
||||
<Controller
|
||||
name="description_html"
|
||||
control={control}
|
||||
render={({ field: { onChange } }) => (
|
||||
render={() => (
|
||||
<DocumentEditorWithRef
|
||||
id={pageId}
|
||||
fileHandler={{
|
||||
cancel: fileService.cancelUpload,
|
||||
delete: fileService.getDeleteImageFunction(workspaceId),
|
||||
@ -134,17 +195,11 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
||||
upload: fileService.getUploadFileFunction(workspaceSlug as string, setIsSubmitting),
|
||||
}}
|
||||
handleEditorReady={handleEditorReady}
|
||||
initialValue={pageDescription ?? "<p></p>"}
|
||||
value={swrPageDetails?.description_html ?? "<p></p>"}
|
||||
value={descriptionYJS}
|
||||
ref={editorRef}
|
||||
containerClassName="p-0 pb-64"
|
||||
editorClassName="lg:px-10 pl-8"
|
||||
onChange={(_description_json, description_html) => {
|
||||
setIsSubmitting("submitting");
|
||||
setShowAlert(true);
|
||||
onChange(description_html);
|
||||
handleSubmit();
|
||||
}}
|
||||
onChange={handleDescriptionChange}
|
||||
mentionHandler={{
|
||||
highlights: mentionHighlights,
|
||||
suggestions: mentionSuggestions,
|
||||
|
@ -50,7 +50,6 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
||||
|
||||
// fetching page details
|
||||
const {
|
||||
data: swrPageDetails,
|
||||
isValidating,
|
||||
error: pageDetailsError,
|
||||
} = useSWR(pageId ? `PAGE_DETAILS_${pageId}` : null, pageId ? () => getPageById(pageId.toString()) : null, {
|
||||
@ -145,7 +144,6 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
||||
/>
|
||||
)}
|
||||
<PageEditorBody
|
||||
swrPageDetails={swrPageDetails}
|
||||
control={control}
|
||||
editorRef={editorRef}
|
||||
handleEditorReady={(val) => setEditorReady(val)}
|
||||
|
@ -31,8 +31,11 @@ export abstract class APIService {
|
||||
);
|
||||
}
|
||||
|
||||
get(url: string, params = {}) {
|
||||
return this.axiosInstance.get(url, params);
|
||||
get(url: string, params = {}, config = {}) {
|
||||
return this.axiosInstance.get(url, {
|
||||
...params,
|
||||
...config,
|
||||
});
|
||||
}
|
||||
|
||||
post(url: string, data = {}, config = {}) {
|
||||
|
@ -119,4 +119,24 @@ export class PageService extends APIService {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async fetchDescriptionYJS(workspaceSlug: string, projectId: string, pageId: string): Promise<any> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/pages/${pageId}/description/`, {
|
||||
headers: {
|
||||
"Content-Type": "application/octet-stream",
|
||||
},
|
||||
})
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async updateDescriptionYJS(workspaceSlug: string, projectId: string, pageId: string, data: any): Promise<any> {
|
||||
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/pages/${pageId}/description/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
68
yarn.lock
68
yarn.lock
@ -2410,6 +2410,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.3.1.tgz#cea919becab684688819b29481a5c43ee1ee9c52"
|
||||
integrity sha512-bVX0EnDZoRXnoA7dyoZe7w2gdRjxmFEcsatHLkcr3R3x4k9oSgZXLe1C2jGbjJWr4j32tYXZ1cpKte6f1WUKzg==
|
||||
|
||||
"@tiptap/extension-collaboration@^2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-collaboration/-/extension-collaboration-2.3.2.tgz#0780eabbe2e72665ed83f86dc70790589d1d0ff1"
|
||||
integrity sha512-1vN+crj5KgqoJhDV+CrfIrBWDIjfpVxiEWHBk+yQU/G2vmyQfbN/R/5gH6rOw5GT3mHqgWFtCDJo4+H/2Ete4w==
|
||||
|
||||
"@tiptap/extension-document@^2.3.1":
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.3.1.tgz#c2c3a1d1f87e262872012508555eda8227a3bc7a"
|
||||
@ -2757,7 +2762,7 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@18.2.48", "@types/react@^18.2.42", "@types/react@^18.2.48":
|
||||
"@types/react@*", "@types/react@^18.2.42", "@types/react@^18.2.48":
|
||||
version "18.2.48"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.48.tgz#11df5664642d0bd879c1f58bc1d37205b064e8f1"
|
||||
integrity sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==
|
||||
@ -5635,6 +5640,11 @@ isexe@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
isomorphic.js@^0.2.4:
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/isomorphic.js/-/isomorphic.js-0.2.5.tgz#13eecf36f2dba53e85d355e11bf9d4208c6f7f88"
|
||||
integrity sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==
|
||||
|
||||
iterator.prototype@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0"
|
||||
@ -5845,6 +5855,13 @@ levn@^0.4.1:
|
||||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lib0@^0.2.42, lib0@^0.2.74, lib0@^0.2.85, lib0@^0.2.86:
|
||||
version "0.2.93"
|
||||
resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.93.tgz#95487c2a97657313cb1d91fbcf9f6d64b7fcd062"
|
||||
integrity sha512-M5IKsiFJYulS+8Eal8f+zAqf5ckm1vffW0fFDxfgxJ+uiVopvDdd3PxJmz0GsVi3YNO7QCFSq0nAsiDmNhLj9Q==
|
||||
dependencies:
|
||||
isomorphic.js "^0.2.4"
|
||||
|
||||
lie@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||
@ -7954,16 +7971,8 @@ streamx@^2.15.0, streamx@^2.16.1:
|
||||
optionalDependencies:
|
||||
bare-events "^2.2.0"
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
|
||||
name string-width-cjs
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -8043,14 +8052,7 @@ stringify-object@^3.3.0:
|
||||
is-obj "^1.0.1"
|
||||
is-regexp "^1.0.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@ -9137,6 +9139,27 @@ wrappy@1:
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
y-indexeddb@^9.0.12:
|
||||
version "9.0.12"
|
||||
resolved "https://registry.yarnpkg.com/y-indexeddb/-/y-indexeddb-9.0.12.tgz#73657f31d52886d7532256610babf5cca4ad5e58"
|
||||
integrity sha512-9oCFRSPPzBK7/w5vOkJBaVCQZKHXB/v6SIT+WYhnJxlEC61juqG0hBrAf+y3gmSMLFLwICNH9nQ53uscuse6Hg==
|
||||
dependencies:
|
||||
lib0 "^0.2.74"
|
||||
|
||||
y-prosemirror@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/y-prosemirror/-/y-prosemirror-1.2.5.tgz#c448f80a6017190bc69a30a33f3930e9924fad3a"
|
||||
integrity sha512-T/JATxC8P2Dbvq/dAiaiztD1a8KEwRP8oLRlT8YlaZdNlLGE1Ea0IJ8If25UlDYmk+4+uqLbqT/S+dzUmwwgbA==
|
||||
dependencies:
|
||||
lib0 "^0.2.42"
|
||||
|
||||
y-protocols@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/y-protocols/-/y-protocols-1.0.6.tgz#66dad8a95752623443e8e28c0e923682d2c0d495"
|
||||
integrity sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==
|
||||
dependencies:
|
||||
lib0 "^0.2.85"
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
@ -9152,6 +9175,13 @@ yaml@^2.3.4:
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362"
|
||||
integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==
|
||||
|
||||
yjs@^13.6.15:
|
||||
version "13.6.15"
|
||||
resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.6.15.tgz#5a2402632aabf83e5baf56342b4c82fe40859306"
|
||||
integrity sha512-moFv4uNYhp8BFxIk3AkpoAnnjts7gwdpiG8RtyFiKbMtxKCS0zVZ5wPaaGpwC3V2N/K8TK8MwtSI3+WO9CHWjQ==
|
||||
dependencies:
|
||||
lib0 "^0.2.86"
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
|
Loading…
Reference in New Issue
Block a user