chore: migrate old data logic

This commit is contained in:
Aaryan Khandelwal 2024-05-15 16:43:35 +05:30
parent f4faa1e0b2
commit b4ccc720b5
16 changed files with 578 additions and 329 deletions

View File

@ -1,5 +1,7 @@
import { generateJSON, getSchema } from "@tiptap/core";
import { Selection } from "@tiptap/pm/state";
import { clsx, type ClassValue } from "clsx";
import { CoreEditorExtensionsWithoutProps } from "src/ui/extensions/core-without-props";
import { twMerge } from "tailwind-merge";
interface EditorClassNames {
noBorder?: boolean;
@ -58,3 +60,15 @@ export const isValidHttpUrl = (string: string): boolean => {
return url.protocol === "http:" || url.protocol === "https:";
};
export const generateJSONfromHTML = (html: string) => {
const extensions = CoreEditorExtensionsWithoutProps();
// @ts-expect-error update types
const contentJSON = generateJSON(html ?? "<p></p>", extensions);
// @ts-expect-error update types
const editorSchema = getSchema(extensions);
return {
contentJSON,
editorSchema,
};
};

View File

@ -0,0 +1,121 @@
import TaskItem from "@tiptap/extension-task-item";
import TaskList from "@tiptap/extension-task-list";
import TextStyle from "@tiptap/extension-text-style";
import TiptapUnderline from "@tiptap/extension-underline";
import Placeholder from "@tiptap/extension-placeholder";
import { Markdown } from "tiptap-markdown";
import { Table } from "src/ui/extensions/table/table";
import { TableCell } from "src/ui/extensions/table/table-cell/table-cell";
import { TableHeader } from "src/ui/extensions/table/table-header/table-header";
import { TableRow } from "src/ui/extensions/table/table-row/table-row";
import { isValidHttpUrl } from "src/lib/utils";
import { CustomCodeBlockExtension } from "src/ui/extensions/code";
import { CustomKeymap } from "src/ui/extensions/keymap";
import { CustomQuoteExtension } from "src/ui/extensions/quote";
import { CustomLinkExtension } from "src/ui/extensions/custom-link";
import { CustomCodeInlineExtension } from "src/ui/extensions/code-inline";
import { CustomTypographyExtension } from "src/ui/extensions/typography";
import { CustomHorizontalRule } from "src/ui/extensions/horizontal-rule/horizontal-rule";
import { CustomCodeMarkPlugin } from "src/ui/extensions/custom-code-inline/inline-code-plugin";
import { MentionsWithoutProps } from "src/ui/mentions/mention-without-props";
import { ImageExtensionWithoutProps } from "src/ui/extensions/image/image-extension-without-props";
import StarterKit from "@tiptap/starter-kit";
export const CoreEditorExtensionsWithoutProps = () => [
StarterKit.configure({
bulletList: {
HTMLAttributes: {
class: "list-disc pl-7 space-y-2",
},
},
orderedList: {
HTMLAttributes: {
class: "list-decimal pl-7 space-y-2",
},
},
listItem: {
HTMLAttributes: {
class: "not-prose space-y-2",
},
},
code: false,
codeBlock: false,
horizontalRule: false,
blockquote: false,
dropcursor: {
color: "rgba(var(--color-text-100))",
width: 1,
},
}),
CustomQuoteExtension,
CustomHorizontalRule.configure({
HTMLAttributes: {
class: "my-4 border-custom-border-400",
},
}),
CustomKeymap,
// ListKeymap,
CustomLinkExtension.configure({
openOnClick: true,
autolink: true,
linkOnPaste: true,
protocols: ["http", "https"],
validate: (url: string) => isValidHttpUrl(url),
HTMLAttributes: {
class:
"text-custom-primary-300 underline underline-offset-[3px] hover:text-custom-primary-500 transition-colors cursor-pointer",
},
}),
CustomTypographyExtension,
ImageExtensionWithoutProps().configure({
HTMLAttributes: {
class: "rounded-md",
},
}),
TiptapUnderline,
TextStyle,
TaskList.configure({
HTMLAttributes: {
class: "not-prose pl-2 space-y-2",
},
}),
TaskItem.configure({
HTMLAttributes: {
class: "flex",
},
nested: true,
}),
CustomCodeBlockExtension.configure({
HTMLAttributes: {
class: "",
},
}),
CustomCodeMarkPlugin,
CustomCodeInlineExtension,
Markdown.configure({
html: true,
transformPastedText: true,
}),
Table,
TableHeader,
TableCell,
TableRow,
MentionsWithoutProps(),
Placeholder.configure({
placeholder: ({ editor, node }) => {
if (node.type.name === "heading") return `Heading ${node.attrs.level}`;
const shouldHidePlaceholder =
editor.isActive("table") || editor.isActive("codeBlock") || editor.isActive("image");
if (shouldHidePlaceholder) return "";
return "Press '/' for commands...";
},
includeChildren: true,
}),
];

View File

@ -0,0 +1,33 @@
import ImageExt from "@tiptap/extension-image";
import { insertLineBelowImageAction } from "./utilities/insert-line-below-image";
import { insertLineAboveImageAction } from "./utilities/insert-line-above-image";
export const ImageExtensionWithoutProps = () =>
ImageExt.extend({
addKeyboardShortcuts() {
return {
ArrowDown: insertLineBelowImageAction,
ArrowUp: insertLineAboveImageAction,
};
},
// storage to keep track of image states Map<src, isDeleted>
addStorage() {
return {
images: new Map<string, boolean>(),
uploadInProgress: false,
};
},
addAttributes() {
return {
...this.parent?.(),
width: {
default: "35%",
},
height: {
default: null,
},
};
},
});

View File

@ -0,0 +1,79 @@
import { CustomMention } from "./custom";
import { ReactRenderer } from "@tiptap/react";
import { Editor } from "@tiptap/core";
import tippy from "tippy.js";
import { MentionList } from "./mention-list";
export const MentionsWithoutProps = () =>
CustomMention.configure({
HTMLAttributes: {
class: "mention",
},
// mentionHighlights: mentionHighlights,
suggestion: {
// @ts-expect-error - Tiptap types are incorrect
render: () => {
let component: ReactRenderer | null = null;
let popup: any | null = null;
return {
onStart: (props: { editor: Editor; clientRect: DOMRect }) => {
if (!props.clientRect) {
return;
}
component = new ReactRenderer(MentionList, {
props: { ...props },
editor: props.editor,
});
props.editor.storage.mentionsOpen = true;
// @ts-expect-error - Tippy types are incorrect
popup = tippy("body", {
getReferenceClientRect: props.clientRect,
appendTo: () => document.querySelector(".active-editor") ?? document.querySelector("#editor-container"),
content: component.element,
showOnCreate: true,
interactive: true,
trigger: "manual",
placement: "bottom-start",
});
},
onUpdate: (props: { editor: Editor; clientRect: DOMRect }) => {
component?.updateProps(props);
if (!props.clientRect) {
return;
}
popup &&
popup[0].setProps({
getReferenceClientRect: props.clientRect,
});
},
onKeyDown: (props: { event: KeyboardEvent }) => {
if (props.event.key === "Escape") {
popup?.[0].hide();
return true;
}
const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"];
if (navigationKeys.includes(props.event.key)) {
// @ts-expect-error - Tippy types are incorrect
component?.ref?.onKeyDown(props);
event?.stopPropagation();
return true;
}
return false;
},
onExit: (props: { editor: Editor; event: KeyboardEvent }) => {
props.editor.storage.mentionsOpen = false;
popup?.[0].destroy();
component?.destroy();
},
};
},
},
});

View File

@ -1,4 +1,4 @@
import { useLayoutEffect, useMemo } from "react";
import { useEffect, useLayoutEffect, useMemo } from "react";
import { EditorProps } from "@tiptap/pm/view";
import { IndexeddbPersistence } from "y-indexeddb";
import * as Y from "yjs";
@ -10,12 +10,11 @@ import { CollaborationProvider } from "src/providers/collaboration-provider";
import { DocumentEditorExtensions } from "src/ui/extensions";
type DocumentEditorProps = {
id?: string;
id: string;
fileHandler: TFileHandler;
value: Uint8Array;
editorClassName: string;
onChange: (binaryString: string) => void;
extensions?: any;
editorProps?: EditorProps;
forwardedRef?: React.MutableRefObject<EditorRefApi | null>;
mentionHandler: {
@ -29,7 +28,7 @@ type DocumentEditorProps = {
};
export const useDocumentEditor = ({
id = "",
id,
editorProps = {},
value,
editorClassName,
@ -52,11 +51,10 @@ export const useDocumentEditor = ({
[id]
);
const yDoc = useMemo(() => {
// update document on value change
useEffect(() => {
if (value.byteLength > 0) Y.applyUpdate(provider.document, value);
return provider.document;
}, [value, provider.document]);
console.log("yDoc", yDoc);
// indexedDB provider
useLayoutEffect(() => {

View File

@ -3,6 +3,8 @@ export { DocumentReadOnlyEditor, DocumentReadOnlyEditorWithRef } from "src/ui/re
// hooks
export { useEditorMarkings } from "src/hooks/use-editor-markings";
// utils
export { proseMirrorJSONToBinaryString } from "src/utils/yjs";
export type { EditorRefApi, EditorReadOnlyRefApi, EditorMenuItem, EditorMenuItemNames } from "@plane/editor-core";

View File

@ -35,6 +35,7 @@ export class CollaborationProvider {
this.configuration.document = configuration.document ?? new Y.Doc();
this.document.on("update", this.documentUpdateHandler.bind(this));
this.document.on("destroy", this.documentDestroyHandler.bind(this));
}
public setConfiguration(configuration: Partial<CompleteCollaboratorProviderConfiguration> = {}): void {
@ -64,4 +65,10 @@ export class CollaborationProvider {
this.timeoutId = null;
}, 2000);
}
documentDestroyHandler() {
if (this.timeoutId !== null) clearTimeout(this.timeoutId);
this.document.off("update", this.documentUpdateHandler);
this.document.off("destroy", this.documentDestroyHandler);
}
}

View File

@ -34,7 +34,6 @@ const DocumentEditor = (props: IDocumentEditor) => {
onChange,
id,
value,
// value,
fileHandler,
containerClassName,
editorClassName = "",

View File

@ -0,0 +1,50 @@
import { Schema } from "@tiptap/pm/model";
import { prosemirrorJSONToYDoc } from "y-prosemirror";
import * as Y from "yjs";
const defaultSchema: Schema = new Schema({
nodes: {
text: {},
doc: { content: "text*" },
},
});
/**
* @description converts ProseMirror JSON to Yjs document
* @param document prosemirror JSON
* @param fieldName
* @param schema
* @returns {Y.Doc} Yjs document
*/
export const proseMirrorJSONToBinaryString = (
document: any,
fieldName: string | Array<string> = "default",
schema?: Schema
): string => {
if (!document) {
throw new Error(
`You've passed an empty or invalid document to the Transformer. Make sure to pass ProseMirror-compatible JSON. Actually passed JSON: ${document}`
);
}
// allow a single field name
if (typeof fieldName === "string") {
const yDoc = prosemirrorJSONToYDoc(schema ?? defaultSchema, document, fieldName);
const docAsUint8Array = Y.encodeStateAsUpdate(yDoc);
const base64Doc = Buffer.from(docAsUint8Array).toString("base64");
return base64Doc;
}
const yDoc = new Y.Doc();
fieldName.forEach((field) => {
const update = Y.encodeStateAsUpdate(prosemirrorJSONToYDoc(schema ?? defaultSchema, document, field));
Y.applyUpdate(yDoc, update);
});
const docAsUint8Array = Y.encodeStateAsUpdate(yDoc);
const base64Doc = Buffer.from(docAsUint8Array).toString("base64");
return base64Doc;
};

View File

@ -1,15 +1,17 @@
import { useEffect } from "react";
import { useEffect, useMemo, useState } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/router";
import useSWR from "swr";
// document editor
// editor
import {
DocumentEditorWithRef,
DocumentReadOnlyEditorWithRef,
EditorReadOnlyRefApi,
EditorRefApi,
IMarking,
proseMirrorJSONToBinaryString,
} from "@plane/document-editor";
import { generateJSONfromHTML } from "@plane/editor-core";
// types
import { IUserLite } from "@plane/types";
// components
@ -53,6 +55,8 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
sidePeekVisible,
updateMarkings,
} = props;
// states
const [isDescriptionReady, setIsDescriptionReady] = useState(false);
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
@ -65,10 +69,10 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
} = useMember();
// derived values
const workspaceId = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "" : "";
const pageId = pageStore?.id ?? "";
const pageId = pageStore?.id;
const pageTitle = pageStore?.name ?? "";
const pageDescription = pageStore?.description_html;
const { description_html, isContentEditable, isSubmitting, updateTitle, setIsSubmitting } = pageStore;
const { isContentEditable, isSubmitting, updateTitle, updateDescription, setIsSubmitting } = pageStore;
const projectMemberIds = projectId ? getProjectMemberIds(projectId.toString()) : [];
const projectMemberDetails = projectMemberIds?.map((id) => getUserDetails(id) as IUserLite);
// use-mention
@ -83,22 +87,41 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
useReloadConfirmations(isSubmitting === "submitting");
const { data: descriptionYJS } = useSWR(
const { data: descriptionYJS, mutate: mutateDescriptionYJS } = useSWR(
workspaceSlug && projectId && pageId ? `PAGE_DESCRIPTION_${workspaceSlug}_${projectId}_${pageId}` : null,
workspaceSlug && projectId && pageId
? () => pageService.fetchDescriptionYJS(workspaceSlug.toString(), projectId.toString(), pageId.toString())
: null,
{
refreshInterval: 20000,
refreshInterval: 15000,
revalidateOnFocus: true,
revalidateOnReconnect: true,
}
);
const pageDescriptionYJS = new Uint8Array(descriptionYJS);
const pageDescriptionYJS = useMemo(
() => (descriptionYJS ? new Uint8Array(descriptionYJS) : undefined),
[descriptionYJS]
);
// if description_yjs field is empty, convert description_html to yDoc and update the DB
// TODO: this is a one-time operation, and needs to be removed once all the pages are updated
useEffect(() => {
if (!pageDescriptionYJS || !pageDescription) return;
if (pageDescriptionYJS.byteLength === 0) {
const { contentJSON, editorSchema } = generateJSONfromHTML(pageDescription ?? "<p></p>");
const yDocBinaryString = proseMirrorJSONToBinaryString(contentJSON, "default", editorSchema);
updateDescription(yDocBinaryString, pageDescription ?? "<p></p>").then(async () => {
await mutateDescriptionYJS();
setIsDescriptionReady(true);
});
} else setIsDescriptionReady(true);
}, [mutateDescriptionYJS, pageDescription, pageDescriptionYJS, updateDescription]);
useEffect(() => {
updateMarkings(description_html ?? "<p></p>");
}, [description_html, updateMarkings]);
updateMarkings(pageDescription ?? "<p></p>");
}, [pageDescription, updateMarkings]);
if (pageId === undefined || !descriptionYJS || !pageDescriptionYJS) return <PageContentLoader />;
if (pageId === undefined || !pageDescriptionYJS || !isDescriptionReady) return <PageContentLoader />;
return (
<div className="flex items-center h-full w-full overflow-y-auto">

View File

@ -4,6 +4,5 @@ export * from "./header";
export * from "./list";
export * from "./loaders";
export * from "./modals";
export * from "./page-detail";
export * from "./pages-list-main-content";
export * from "./pages-list-view";

View File

@ -2,27 +2,116 @@
import { Loader } from "@plane/ui";
export const PageContentLoader = () => (
<div className="flex">
<div className="w-[5%]" />
<Loader className="flex-shrink-0 flex-grow">
<div className="mt-10 space-y-2">
<Loader.Item height="20px" />
<Loader.Item height="20px" width="80%" />
<Loader.Item height="20px" width="80%" />
<div className="relative w-full h-full flex flex-col">
{/* header */}
<div className="px-4 flex-shrink-0 relative flex items-center justify-between h-12 border-b border-custom-border-100">
{/* left options */}
<Loader className="flex-shrink-0 w-[280px]">
<Loader.Item width="26px" height="26px" />
</Loader>
{/* editor options */}
<div className="w-full relative flex items-center divide-x divide-custom-border-100">
<Loader className="relative flex items-center gap-1 pr-2">
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
<Loader className="relative flex items-center gap-1 px-2">
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
<Loader className="relative flex items-center gap-1 px-2">
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
<Loader className="relative flex items-center gap-1 pl-2">
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
</div>
<div className="mt-12 space-y-10">
{Array.from(Array(4)).map((i) => (
<div key={i}>
<Loader.Item height="25px" width="20%" />
<div className="mt-5 space-y-3">
<Loader.Item height="15px" width="40%" />
<Loader.Item height="15px" width="30%" />
<Loader.Item height="15px" width="35%" />
{/* right options */}
<Loader className="w-full relative flex justify-end items-center gap-1">
<Loader.Item width="60px" height="26px" />
<Loader.Item width="40px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
</div>
{/* content */}
<div className="px-4 w-full h-full overflow-hidden relative flex">
{/* table of content loader */}
<div className="flex-shrink-0 w-[280px] pr-5 py-5">
<Loader className="w-full space-y-4">
<Loader.Item width="100%" height="24px" />
<div className="space-y-2">
<Loader.Item width="60%" height="12px" />
<div className="ml-6 space-y-2">
<Loader.Item width="80%" height="12px" />
<Loader.Item width="100%" height="12px" />
</div>
<Loader.Item width="60%" height="12px" />
<div className="ml-6 space-y-2">
<Loader.Item width="80%" height="12px" />
<Loader.Item width="100%" height="12px" />
</div>
<Loader.Item width="100%" height="12px" />
<Loader.Item width="60%" height="12px" />
<div className="ml-6 space-y-2">
<Loader.Item width="80%" height="12px" />
<Loader.Item width="100%" height="12px" />
</div>
<Loader.Item width="80%" height="12px" />
<Loader.Item width="100%" height="12px" />
</div>
</Loader>
</div>
{/* editor loader */}
<div className="w-full h-full py-5">
<Loader className="relative space-y-4">
<Loader.Item width="50%" height="36px" />
<div className="space-y-2">
<div className="py-2">
<Loader.Item width="100%" height="36px" />
</div>
<Loader.Item width="80%" height="22px" />
<div className="relative flex items-center gap-2">
<Loader.Item width="30px" height="30px" />
<Loader.Item width="30%" height="22px" />
</div>
<div className="py-2">
<Loader.Item width="60%" height="36px" />
</div>
<Loader.Item width="70%" height="22px" />
<Loader.Item width="30%" height="22px" />
<div className="relative flex items-center gap-2">
<Loader.Item width="30px" height="30px" />
<Loader.Item width="30%" height="22px" />
</div>
<div className="py-2">
<Loader.Item width="50%" height="30px" />
</div>
<Loader.Item width="100%" height="22px" />
<div className="py-2">
<Loader.Item width="30%" height="30px" />
</div>
<Loader.Item width="30%" height="22px" />
<div className="relative flex items-center gap-2">
<div className="py-2">
<Loader.Item width="30px" height="30px" />
</div>
<Loader.Item width="30%" height="22px" />
</div>
</div>
))}
</Loader>
</div>
</Loader>
<div className="w-[5%]" />
</div>
</div>
);

View File

@ -1,3 +0,0 @@
export * from "./loader";
export * from "./root";

View File

@ -1,118 +0,0 @@
import { FC } from "react";
// components/ui
import { Loader } from "@plane/ui";
export const PageDetailRootLoader: FC = () => (
<div className=" relative w-full h-full flex flex-col">
{/* header */}
<div className="px-4 flex-shrink-0 relative flex items-center justify-between h-12 border-b border-custom-border-100">
{/* left options */}
<Loader className="flex-shrink-0 w-[280px]">
<Loader.Item width="26px" height="26px" />
</Loader>
{/* editor options */}
<div className="w-full relative flex items-center divide-x divide-custom-border-100">
<Loader className="relative flex items-center gap-1 pr-2">
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
<Loader className="relative flex items-center gap-1 px-2">
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
<Loader className="relative flex items-center gap-1 px-2">
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
<Loader className="relative flex items-center gap-1 pl-2">
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
</div>
{/* right options */}
<Loader className="w-full relative flex justify-end items-center gap-1">
<Loader.Item width="60px" height="26px" />
<Loader.Item width="40px" height="26px" />
<Loader.Item width="26px" height="26px" />
<Loader.Item width="26px" height="26px" />
</Loader>
</div>
{/* content */}
<div className="px-4 w-full h-full overflow-hidden relative flex">
{/* table of content loader */}
<div className="flex-shrink-0 w-[280px] pr-5 py-5">
<Loader className="w-full space-y-4">
<Loader.Item width="100%" height="24px" />
<div className="space-y-2">
<Loader.Item width="60%" height="12px" />
<div className="ml-6 space-y-2">
<Loader.Item width="80%" height="12px" />
<Loader.Item width="100%" height="12px" />
</div>
<Loader.Item width="60%" height="12px" />
<div className="ml-6 space-y-2">
<Loader.Item width="80%" height="12px" />
<Loader.Item width="100%" height="12px" />
</div>
<Loader.Item width="100%" height="12px" />
<Loader.Item width="60%" height="12px" />
<div className="ml-6 space-y-2">
<Loader.Item width="80%" height="12px" />
<Loader.Item width="100%" height="12px" />
</div>
<Loader.Item width="80%" height="12px" />
<Loader.Item width="100%" height="12px" />
</div>
</Loader>
</div>
{/* editor loader */}
<div className="w-full h-full py-5">
<Loader className="relative space-y-4">
<Loader.Item width="50%" height="36px" />
<div className="space-y-2">
<div className="py-2">
<Loader.Item width="100%" height="36px" />
</div>
<Loader.Item width="80%" height="22px" />
<div className="relative flex items-center gap-2">
<Loader.Item width="30px" height="30px" />
<Loader.Item width="30%" height="22px" />
</div>
<div className="py-2">
<Loader.Item width="60%" height="36px" />
</div>
<Loader.Item width="70%" height="22px" />
<Loader.Item width="30%" height="22px" />
<div className="relative flex items-center gap-2">
<Loader.Item width="30px" height="30px" />
<Loader.Item width="30%" height="22px" />
</div>
<div className="py-2">
<Loader.Item width="50%" height="30px" />
</div>
<Loader.Item width="100%" height="22px" />
<div className="py-2">
<Loader.Item width="30%" height="30px" />
</div>
<Loader.Item width="30%" height="22px" />
<div className="relative flex items-center gap-2">
<div className="py-2">
<Loader.Item width="30px" height="30px" />
</div>
<Loader.Item width="30%" height="22px" />
</div>
</div>
</Loader>
</div>
</div>
</div>
);

View File

@ -1,54 +0,0 @@
import { FC, Fragment } from "react";
import { observer } from "mobx-react-lite";
// hooks
import { PageHead } from "@/components/core";
import { useProjectPages, usePage } from "@/hooks/store";
// components
import { PageDetailRootLoader } from "./";
type TPageDetailRoot = {
projectId: string;
pageId: string;
};
export const PageDetailRoot: FC<TPageDetailRoot> = observer((props) => {
const { projectId, pageId } = props;
// hooks
const { loader } = useProjectPages(projectId);
const { id, name } = usePage(pageId);
if (loader === "init-loader") return <PageDetailRootLoader />;
if (!id) return <div className="">No page is available.</div>;
return (
<Fragment>
<PageHead title={name || "Pages"} />
<div className="relative w-full h-full flex flex-col">
<div className="flex-shrink-0 px-4 relative flex items-center justify-between h-12 border-b border-custom-border-100">
{/* header left container */}
<div className="flex-shrink-0 w-[280px]">Icon</div>
{/* header editor tool container */}
<div className="w-full relative hidden md:flex items-center divide-x divide-custom-border-100 ">
Editor keys
</div>
{/* header right operations container */}
<div className="w-full relative flex justify-end">right saved</div>
</div>
{/* editor container for small screens */}
<div className="px-4 h-12 relative flex md:hidden items-center border-b border-custom-border-100">
Editor keys
</div>
<div className="px-4 w-full h-full overflow-hidden relative flex">
{/* editor table of content content container */}
<div className="flex-shrink-0 w-[280px] pr-5 py-5">Table of content</div>
{/* editor container */}
<div className="w-full h-full py-5">Editor Container</div>
</div>
</div>
</Fragment>
);
});

244
yarn.lock
View File

@ -2373,20 +2373,25 @@
dependencies:
tippy.js "^6.3.1"
"@tiptap/core@^2.1.13", "@tiptap/core@^2.3.1":
"@tiptap/core@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.3.1.tgz#e6980554882899b2c600f40b688c33b6b58628c2"
integrity sha512-ycpQlmczAOc05TgB5sc3RUTEEBXAVmS8MR9PqQzg96qidaRfVkgE+2w4k7t83PMHl2duC0MGqOCy96pLYwSpeg==
"@tiptap/extension-blockquote@^2.1.13", "@tiptap/extension-blockquote@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.3.1.tgz#df779c2143a445f741a6277ef134b705d52bbc8b"
integrity sha512-eyw3/Zn/XbIP2Yo11iE4vYcJ0471aBPMLD56YOyUC0PIF7D5tvPutDesSg95R+BDa5Tq/Id2zV5pZerw1dwwOQ==
"@tiptap/core@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.4.0.tgz#6f8eee8beb5b89363582366b201ccc4798ac98a9"
integrity sha512-YJSahk8pkxpCs8SflCZfTnJpE7IPyUWIylfgXM2DefjRQa5DZ+c6sNY0s/zbxKYFQ6AuHVX40r9pCfcqHChGxQ==
"@tiptap/extension-bold@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.3.1.tgz#73ccc70b339b95bc68f452851933fdd1d45c7c98"
integrity sha512-szHDXKOQfrlCzsys401zBtPWE5gyY3LcpPlrn2zBRrBmzU2U/1A7Y3HkoqZo3SSrTY37eG1Vr2J2aHySK6Uj/w==
"@tiptap/extension-blockquote@^2.1.13", "@tiptap/extension-blockquote@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.4.0.tgz#0179076ea2fa12e41a198dad087b81d368653b8d"
integrity sha512-nJJy4KsPgQqWTTDOWzFRdjCfG5+QExfZj44dulgDFNh+E66xhamnbM70PklllXJgEcge7xmT5oKM0gKls5XgFw==
"@tiptap/extension-bold@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.4.0.tgz#b5ced2c3bf51f304890137dbdf394d58c01eb208"
integrity sha512-csnW6hMDEHoRfxcPRLSqeJn+j35Lgtt1YRiOwn7DlS66sAECGRuoGfCvQSPij0TCDp4VCR9if5Sf8EymhnQumQ==
"@tiptap/extension-bubble-menu@^2.3.1":
version "2.3.1"
@ -2395,35 +2400,35 @@
dependencies:
tippy.js "^6.3.7"
"@tiptap/extension-bullet-list@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.3.1.tgz#7156e4b8898da8b27c5272476d4f8596d8f633cc"
integrity sha512-pif0AB4MUoA1Xm26y1ovH7vfXaV19T9EEQH4tgN2g2eTfdFnQWDmKI0r3XRxudtg40RstBJRa81N9xEO79o8ag==
"@tiptap/extension-bullet-list@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.4.0.tgz#60eea05b5ac8c8e8d615c057559fddb95033abeb"
integrity sha512-9S5DLIvFRBoExvmZ+/ErpTvs4Wf1yOEs8WXlKYUCcZssK7brTFj99XDwpHFA29HKDwma5q9UHhr2OB2o0JYAdw==
"@tiptap/extension-code-block@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.3.1.tgz#1a3e80133ccd67bd06e1d8383563430e648c3e21"
integrity sha512-rM7T+DWuOShariPl5vknNFMesPOFQrhMjmms9Ql636sSxOcnkb0d39NFbUpI/r5noFDC6Km+lAebF0Rx2MxpKQ==
"@tiptap/extension-code-block@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.4.0.tgz#b7f1da4825677a2ea6b8e970a1197877551e5dc8"
integrity sha512-QWGdv1D56TBGbbJSj2cIiXGJEKguPiAl9ONzJ/Ql1ZksiQsYwx0YHriXX6TOC//T4VIf6NSClHEtwtxWBQ/Csg==
"@tiptap/extension-code@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.3.1.tgz#cea919becab684688819b29481a5c43ee1ee9c52"
integrity sha512-bVX0EnDZoRXnoA7dyoZe7w2gdRjxmFEcsatHLkcr3R3x4k9oSgZXLe1C2jGbjJWr4j32tYXZ1cpKte6f1WUKzg==
"@tiptap/extension-code@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.4.0.tgz#3a9fed3585bf49f445505c2e9ad71fd66e117304"
integrity sha512-wjhBukuiyJMq4cTcK3RBTzUPV24k5n1eEPlpmzku6ThwwkMdwynnMGMAmSF3fErh3AOyOUPoTTjgMYN2d10SJA==
"@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==
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-collaboration/-/extension-collaboration-2.4.0.tgz#d830694ac61a4b9857ffb77f24585e13a9cd6a0c"
integrity sha512-achU+GU9tqxn3zsU61CbwWrCausf0U23MJIpo8vnywOIx6E955by6okHEHoUazLIGVFXVc5DBzBP7bf+Snzk0Q==
"@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"
integrity sha512-uWYbzAV95JnetFBduWRI9n2QbQfmznQ7I6XzfZxuTAO2KcWGvHPBS7F00COO9Y67FZAPMbuQ1njtCJK0nClOPw==
"@tiptap/extension-document@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.4.0.tgz#a396b2cbcc8708aa2a0a41d0be481fda4b61c77b"
integrity sha512-3jRodQJZDGbXlRPERaloS+IERg/VwzpC1IO6YSJR9jVIsBO6xC29P3cKTQlg1XO7p6ZH/0ksK73VC5BzzTwoHg==
"@tiptap/extension-dropcursor@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.3.1.tgz#2a594264c9b56c23d9a8e46e8538d6b72860e10f"
integrity sha512-xDG1Z01ftRI4mIOY+bPuG53xZ9FfVd6hzjNchwFHRlU3E+/2O+DsEBy/pJuHmpnFx1B/1ANbssoidGvK3LIPYw==
"@tiptap/extension-dropcursor@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.4.0.tgz#8f54908f84a4ab7d2d7de7fc0197511138445740"
integrity sha512-c46HoG2PEEpSZv5rmS5UX/lJ6/kP1iVO0Ax+6JrNfLEIiDULUoi20NqdjolEa38La2VhWvs+o20OviiTOKEE9g==
"@tiptap/extension-floating-menu@^2.3.1":
version "2.3.1"
@ -2432,95 +2437,95 @@
dependencies:
tippy.js "^6.3.7"
"@tiptap/extension-gapcursor@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.3.1.tgz#a31d87995ca7d8b58af27e55a14e6eb6b5c0a2c5"
integrity sha512-jhMw0LtEV/HVovUDRdoH0QLnBWLDyw4Su7UZ0bkMtsnCO9MujLKths3SKsPstuAckZQKR5smokEytxDHH0aglg==
"@tiptap/extension-gapcursor@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.4.0.tgz#2a738509d40f5f856492c11e32b10e4462f71216"
integrity sha512-F4y/0J2lseohkFUw9P2OpKhrJ6dHz69ZScABUvcHxjznJLd6+0Zt7014Lw5PA8/m2d/w0fX8LZQ88pZr4quZPQ==
"@tiptap/extension-hard-break@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.3.1.tgz#c174915cc32865f9c742104d1c75ffa3afc477f4"
integrity sha512-HO47iS2KQJLxhZM4ghZz5t2qgESH6D/mKJbjO7jM0eCYEyUfPyYJwV2VgjQP7x+1axcvsrhpzkJrjSg5+KqtQQ==
"@tiptap/extension-hard-break@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.4.0.tgz#b5bf5b065827280e450fba8f53d137654509d836"
integrity sha512-3+Z6zxevtHza5IsDBZ4lZqvNR3Kvdqwxq/QKCKu9UhJN1DUjsg/l1Jn2NilSQ3NYkBYh2yJjT8CMo9pQIu776g==
"@tiptap/extension-heading@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.3.1.tgz#ddc5810e41cff528924ac873ff444bb8d715742d"
integrity sha512-epdIrg1xpuk5ApnNyM/NJO1dhVZgD7kDPem6QH4fug5UJtCueze942yNzUhCuvckmIegfdferAb1p4ug4674ig==
"@tiptap/extension-heading@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.4.0.tgz#16302ce691714244c3d3fa92d2db86a5c895a025"
integrity sha512-fYkyP/VMo7YHO76YVrUjd95Qeo0cubWn/Spavmwm1gLTHH/q7xMtbod2Z/F0wd6QHnc7+HGhO7XAjjKWDjldaw==
"@tiptap/extension-history@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.3.1.tgz#de6ec11ac686856ea1f0806b69452f2e39696baf"
integrity sha512-m+W6qTP4V0PHqqKnXw/ma18a62O0Cqp5FDWtSarOuxx6W4FpVr4A3Uxfbp4RigZEYanLcX4UJOWL4nWsFdYWHw==
"@tiptap/extension-history@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.4.0.tgz#1dbf8410c091175627414d48a0d857232a8f4094"
integrity sha512-gr5qsKAXEVGr1Lyk1598F7drTaEtAxqZiuuSwTCzZzkiwgEQsWMWTWc9F8FlneCEaqe1aIYg6WKWlmYPaFwr0w==
"@tiptap/extension-horizontal-rule@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.3.1.tgz#82330ab9a6a27484145bc73b9b7d23dce0e5594f"
integrity sha512-IPgCFkiT6Y5BSFBQMTXS6gq2Ust6otMzRwddoI0RC8tl/tMftFBEPqYKADWVQeQb4C6AQydRjUbmAwHpBH31Eg==
"@tiptap/extension-horizontal-rule@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.4.0.tgz#7f27c0778004602686251af7e2f7a8461a3d77ba"
integrity sha512-yDgxy+YxagcEsBbdWvbQiXYxsv3noS1VTuGwc9G7ZK9xPmBHJ5y0agOkB7HskwsZvJHoaSqNRsh7oZTkf0VR3g==
"@tiptap/extension-image@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.3.1.tgz#3dd730a69bb2fc432b908ca5231cb32f7dcce77e"
integrity sha512-3RhVBySQA2LbftWhtZ0p2Mqf9lihNAYs3uQ3iyaB+BYViQiHyVpui09Wny0BwNy0oV6ryUWjBifko2Z1AZgANw==
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.4.0.tgz#21a18e80ed6bc330cf8ab2ca990a3addb40916c8"
integrity sha512-NIVhRPMO/ONo8OywEd+8zh0Q6Q7EbFHtBxVsvfOKj9KtZkaXQfUO4MzONTyptkvAchTpj9pIzeaEY5fyU87gFA==
"@tiptap/extension-italic@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.3.1.tgz#376a1957dff7b687ce247f8a79dc397cb9c7565a"
integrity sha512-yEAn0dT1LH1vAULmZv3L1fs7M1Fn/8wZCw7LDGw2/E+VYbDeXgy7XwMPyzhrzV1oV9Z+3gugCbYV0IJ4PBwudA==
"@tiptap/extension-italic@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.4.0.tgz#42ab003e04e1e8d825f698914c0e80ac849144f1"
integrity sha512-aaW/L9q+KNHHK+X73MPloHeIsT191n3VLd3xm6uUcFDnUNvzYJ/q65/1ZicdtCaOLvTutxdrEvhbkrVREX6a8g==
"@tiptap/extension-list-item@^2.1.13", "@tiptap/extension-list-item@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.3.1.tgz#241970a6fe50aa3d5c0a28efa9ba05f31e2f0f12"
integrity sha512-GyHLNoXVo9u29NVqijwZPBcv9MzXMGyIiQiO5FxRpuT4Ei4ZmsaJrJ2dmhO3KZhX0HdTSc65/omM2XBr6PDoLA==
"@tiptap/extension-list-item@^2.1.13", "@tiptap/extension-list-item@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.4.0.tgz#a97a48850b81e94b9a60cc2aa16e515aa5311456"
integrity sha512-reUVUx+2cI2NIAqMZhlJ9uK/+zvRzm1GTmlU2Wvzwc7AwLN4yemj6mBDsmBLEXAKPvitfLh6EkeHaruOGymQtg==
"@tiptap/extension-mention@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.3.1.tgz#bdc8bbc2f7b1e376d88f074cdc1b04af856985de"
integrity sha512-60N1L9bTPVsE6zPDtYQqpZGZNuFpFfw4Opd26Xnfuwx14xvFLC34gDN7hpwVgqncVg9CefFigi1o9L5C+mXDBg==
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.4.0.tgz#35f13d71e207280cafe5b00e76f17b4c372fbe8b"
integrity sha512-7BqCNfqF1Mv9IrtdlHADwXMFo968UNmthf/TepVXC7EX2Ke6/Y4vvxmpYVNZc55FdswFwpVyZ2VeXBj3AC2JcA==
"@tiptap/extension-ordered-list@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.3.1.tgz#964bcc082e08a9019359ecd097d0aab608bcc166"
integrity sha512-+6I76b7fu0FghUtzB0LyIC5GB0xfrpAKtXjbrmeUGsOEL7jxKsE6+A5RoTrgQTfuP7oItdCZGTSC/8WtGbtEMg==
"@tiptap/extension-ordered-list@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.4.0.tgz#6cf82e10d7e7f7cc44156d29b0b71a22dec31612"
integrity sha512-Zo0c9M0aowv+2+jExZiAvhCB83GZMjZsxywmuOrdUbq5EGYKb7q8hDyN3hkrktVHr9UPXdPAYTmLAHztTOHYRA==
"@tiptap/extension-paragraph@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.3.1.tgz#2aa53a06cfad637640bf65a7cd0ab2ef5f5a5c10"
integrity sha512-bHkkHU012clwCrpzmEHGuF8fwLuFL3x9MJ17wnhwanoIM3MG6ZCdeb9copjDvUpZXLKTUYKotoPGNhxmOrP2bQ==
"@tiptap/extension-paragraph@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.4.0.tgz#5b9aea8775937b327bbe6754be12ae3144fb09ff"
integrity sha512-+yse0Ow67IRwcACd9K/CzBcxlpr9OFnmf0x9uqpaWt1eHck1sJnti6jrw5DVVkyEBHDh/cnkkV49gvctT/NyCw==
"@tiptap/extension-placeholder@^2.3.0":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.3.1.tgz#08fe48ff5bb75b1be64c50463d5022894293fd63"
integrity sha512-iqmwqT+pBaWcL6Bj8ht+SKzFGxEMfAPEKOlxIrfaY/um80q5kmyrmes6LOSAHTylsm3kmna6s0p2TD2zcnBQqw==
"@tiptap/extension-strike@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.3.1.tgz#2e47c711b85f46c7e575b0b154cedc93ab9bc89b"
integrity sha512-fpsVewcnaYk37TAF4JHkwH9O6Ml7JooF1v/Eh9p7PSItNcEfg/3RLlJL3c53RzLWdlunjgptM/M0alPV0Zyq4A==
"@tiptap/extension-strike@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.4.0.tgz#f09c4f51f7fed01c356026d7e8d8a1d1f2ac8f18"
integrity sha512-pE1uN/fQPOMS3i+zxPYMmPmI3keubnR6ivwM+KdXWOMnBiHl9N4cNpJgq1n2eUUGKLurC2qrQHpnVyGAwBS6Vg==
"@tiptap/extension-task-item@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.3.1.tgz#6ff237bad42e5184d01bb327a677471073aa7a06"
integrity sha512-iNVLiwJOTp9UulUS6tLk5NR85nNxtxqvaboOwPxoqcFaM/IkybTwZ/hMr9EqbAucigx85OowHKsdgPHIAr3xdw==
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.4.0.tgz#33227e72fcffdf087446f88cdb7a10feab4c2087"
integrity sha512-x40vdHnmDiBbA2pjWR/92wVGb6jT13Nk2AhRUI/oP/r4ZGKpTypoB7heDnvLBgH0Y5a51dFqU+G1SFFL30u5uA==
"@tiptap/extension-task-list@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.3.1.tgz#dc92096dbe91c6a4f744f9d8e19c9ebde0e30132"
integrity sha512-lu/27tetu2KYEjsaD8wyQ4rBthxrW8aRNeSv74jXJLPYN4aCtAl9C2bM7os+A2OYpidPBMbRjp8ZQoUnJ9auNA==
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.4.0.tgz#61a500fe4a89d5c789ad4fb64c8d7eeedfe26b63"
integrity sha512-vmUB3wEJU81QbiHUygBlselQW8YIW8/85UTwANvWx8+KEWyM7EUF4utcm5R2UobIprIcWb4hyVkvW/5iou25gg==
"@tiptap/extension-text-style@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.3.1.tgz#e9f2b7f1640d32b17c089fa089f595aef1334c22"
integrity sha512-eXtuf3AqcOv28BM0dO4lbBNnvM1fo4WWuT+/s1YV5Ovex3T5OS7PsPuR/9p5AD4NuX9QvNrV+eM02mcNzaTBWw==
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.4.0.tgz#9f86d8de4606bc37090b7b02c2aaf40bb37a860f"
integrity sha512-H0uPWeZ4sXz3o836TDWnpd38qClqzEM2d6QJ9TK+cQ1vE5Gp8wQ5W4fwUV1KAHzpJKE/15+BXBjLyVYQdmXDaQ==
"@tiptap/extension-text@^2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.3.1.tgz#c5ded941eab937ea4743b88ff27c5eac971fe3db"
integrity sha512-ZM+Bpty9jChEN/VjUP/fX1Fvoz0Z3YLdjj9+pFA0H7woli+TmxWY6yUUTA2SBDb2mJ52yNOUfRE/sYx6gkDuBQ==
"@tiptap/extension-text@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.4.0.tgz#a3a5f45a9856d513e574f24e2c9b6028273f8eb3"
integrity sha512-LV0bvE+VowE8IgLca7pM8ll7quNH+AgEHRbSrsI3SHKDCYB9gTHMjWaAkgkUVaO1u0IfCrjnCLym/PqFKa+vvg==
"@tiptap/extension-underline@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.3.1.tgz#90a594cc69644464f1070b4bd36ed4bed4ce3c38"
integrity sha512-xgLGr7bM5OAKagUKdL5dWxJHgwEp2fk3D5XCVUBwqgeOZtOFteoqPzb/2617w7qrP+9oM9zRjw6z27hM8YxyvQ==
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.4.0.tgz#fb554333aed8a9ac1400b94f362a774c650f5a90"
integrity sha512-guWojb7JxUwLz4OKzwNExJwOkhZjgw/ttkXCMBT0PVe55k998MMYe1nvN0m2SeTW9IxurEPtScH4kYJ0XuSm8Q==
"@tiptap/pm@^2.1.13":
version "2.3.1"
@ -2555,31 +2560,36 @@
"@tiptap/extension-floating-menu" "^2.3.1"
"@tiptap/starter-kit@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.3.1.tgz#b7356eb667ae85552a6f666b73daabb3185e1271"
integrity sha512-VGk1o5y5f2ZHKkvP2WNj8BH7FGak0d0cjxQiXP1n5w8eS0vFnTkCz3JbCPM+KTKobsBmxd2vSC3ElgP9E9d2xw==
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.4.0.tgz#ad2c2d900af41e55eaaccafa92fd6b2acaebd97e"
integrity sha512-DYYzMZdTEnRn9oZhKOeRCcB+TjhNz5icLlvJKoHoOGL9kCbuUyEf8WRR2OSPckI0+KUIPJL3oHRqO4SqSdTjfg==
dependencies:
"@tiptap/core" "^2.3.1"
"@tiptap/extension-blockquote" "^2.3.1"
"@tiptap/extension-bold" "^2.3.1"
"@tiptap/extension-bullet-list" "^2.3.1"
"@tiptap/extension-code" "^2.3.1"
"@tiptap/extension-code-block" "^2.3.1"
"@tiptap/extension-document" "^2.3.1"
"@tiptap/extension-dropcursor" "^2.3.1"
"@tiptap/extension-gapcursor" "^2.3.1"
"@tiptap/extension-hard-break" "^2.3.1"
"@tiptap/extension-heading" "^2.3.1"
"@tiptap/extension-history" "^2.3.1"
"@tiptap/extension-horizontal-rule" "^2.3.1"
"@tiptap/extension-italic" "^2.3.1"
"@tiptap/extension-list-item" "^2.3.1"
"@tiptap/extension-ordered-list" "^2.3.1"
"@tiptap/extension-paragraph" "^2.3.1"
"@tiptap/extension-strike" "^2.3.1"
"@tiptap/extension-text" "^2.3.1"
"@tiptap/core" "^2.4.0"
"@tiptap/extension-blockquote" "^2.4.0"
"@tiptap/extension-bold" "^2.4.0"
"@tiptap/extension-bullet-list" "^2.4.0"
"@tiptap/extension-code" "^2.4.0"
"@tiptap/extension-code-block" "^2.4.0"
"@tiptap/extension-document" "^2.4.0"
"@tiptap/extension-dropcursor" "^2.4.0"
"@tiptap/extension-gapcursor" "^2.4.0"
"@tiptap/extension-hard-break" "^2.4.0"
"@tiptap/extension-heading" "^2.4.0"
"@tiptap/extension-history" "^2.4.0"
"@tiptap/extension-horizontal-rule" "^2.4.0"
"@tiptap/extension-italic" "^2.4.0"
"@tiptap/extension-list-item" "^2.4.0"
"@tiptap/extension-ordered-list" "^2.4.0"
"@tiptap/extension-paragraph" "^2.4.0"
"@tiptap/extension-strike" "^2.4.0"
"@tiptap/extension-text" "^2.4.0"
"@tiptap/suggestion@^2.0.13", "@tiptap/suggestion@^2.1.13":
"@tiptap/suggestion@^2.0.13":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.4.0.tgz#1926cde5f197d116baf7794f55bd971245540e5c"
integrity sha512-6dCkjbL8vIzcLWtS6RCBx0jlYPKf2Beuyq5nNLrDDZZuyJow5qJAY0eGu6Xomp9z0WDK/BYOxT4hHNoGMDkoAg==
"@tiptap/suggestion@^2.1.13":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.3.1.tgz#b0ae3678214240a066b7c0c415a5ca9981819855"
integrity sha512-hfUIsC80QivPH833rlqh3x1RCOat2mE0SzR6m2Z1ZNZ86N5BrYDU8e8p81gR//4SlNBJ7BZGVWN3DXj+aAKs2A==