From ca68a864944ed49e9b09743c12b6cedfbcc8ed2f Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Thu, 10 Oct 2024 18:28:13 +0530 Subject: [PATCH 01/12] regression: image aspect ratio fix --- .../custom-image/components/image-block.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index ed60f3dab08..35e75596839 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -62,7 +62,7 @@ export const CustomImageBlock: React.FC = (props) => { const [size, setSize] = useState({ width: ensurePixelString(width, "35%"), height: ensurePixelString(height, "auto"), - aspectRatio: aspectRatio || 1, + aspectRatio: aspectRatio || null, }); const [isResizing, setIsResizing] = useState(false); const [initialResizeComplete, setInitialResizeComplete] = useState(false); @@ -102,17 +102,17 @@ export const CustomImageBlock: React.FC = (props) => { } setEditorContainer(closestEditorContainer); - const aspectRatio = img.naturalWidth / img.naturalHeight; + const aspectRatioCalculated = img.naturalWidth / img.naturalHeight; if (width === "35%") { const editorWidth = closestEditorContainer.clientWidth; const initialWidth = Math.max(editorWidth * 0.35, MIN_SIZE); - const initialHeight = initialWidth / aspectRatio; + const initialHeight = initialWidth / aspectRatioCalculated; const initialComputedSize = { width: `${Math.round(initialWidth)}px` satisfies Pixel, height: `${Math.round(initialHeight)}px` satisfies Pixel, - aspectRatio: aspectRatio, + aspectRatio: aspectRatioCalculated, }; setSize(initialComputedSize); @@ -122,9 +122,10 @@ export const CustomImageBlock: React.FC = (props) => { ); } else { // as the aspect ratio in not stored for old images, we need to update the attrs - if (!aspectRatio) { + // or if aspectRatioCalculated from the image's width and height doesn't match stored aspectRatio then also we'll update the attrs + if (!aspectRatio || aspectRatio !== aspectRatioCalculated) { setSize((prevSize) => { - const newSize = { ...prevSize, aspectRatio }; + const newSize = { ...prevSize, aspectRatio: aspectRatioCalculated }; updateAttributesSafely( newSize, "Failed to update attributes while initializing images with width but no aspect ratio:" @@ -215,7 +216,7 @@ export const CustomImageBlock: React.FC = (props) => { onMouseDown={handleImageMouseDown} style={{ width: size.width, - aspectRatio: size.aspectRatio, + ...(size.aspectRatio && { aspectRatio: size.aspectRatio }), }} > {showImageLoader && ( @@ -241,7 +242,7 @@ export const CustomImageBlock: React.FC = (props) => { })} style={{ width: size.width, - aspectRatio: size.aspectRatio, + ...(size.aspectRatio && { aspectRatio: size.aspectRatio }), }} /> {showImageUtils && ( From a3646bf2f53d731c72dc4924ad1e9b8dd5f13fd4 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Thu, 10 Oct 2024 20:09:14 +0530 Subject: [PATCH 02/12] fix: name of variables changed for clarity --- .../custom-image/components/image-block.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 35e75596839..c7008db00ef 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -57,12 +57,12 @@ export const CustomImageBlock: React.FC = (props) => { editorContainer, setEditorContainer, } = props; - const { src: remoteImageSrc, width, height, aspectRatio } = node.attrs; + const { src: remoteImageSrc, width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs; // states const [size, setSize] = useState({ - width: ensurePixelString(width, "35%"), - height: ensurePixelString(height, "auto"), - aspectRatio: aspectRatio || null, + width: ensurePixelString(nodeWidth, "35%"), + height: ensurePixelString(nodeHeight, "auto"), + aspectRatio: nodeAspectRatio || null, }); const [isResizing, setIsResizing] = useState(false); const [initialResizeComplete, setInitialResizeComplete] = useState(false); @@ -104,7 +104,7 @@ export const CustomImageBlock: React.FC = (props) => { setEditorContainer(closestEditorContainer); const aspectRatioCalculated = img.naturalWidth / img.naturalHeight; - if (width === "35%") { + if (nodeWidth === "35%") { const editorWidth = closestEditorContainer.clientWidth; const initialWidth = Math.max(editorWidth * 0.35, MIN_SIZE); const initialHeight = initialWidth / aspectRatioCalculated; @@ -123,7 +123,7 @@ export const CustomImageBlock: React.FC = (props) => { } else { // as the aspect ratio in not stored for old images, we need to update the attrs // or if aspectRatioCalculated from the image's width and height doesn't match stored aspectRatio then also we'll update the attrs - if (!aspectRatio || aspectRatio !== aspectRatioCalculated) { + if (!nodeAspectRatio || nodeAspectRatio !== aspectRatioCalculated) { setSize((prevSize) => { const newSize = { ...prevSize, aspectRatio: aspectRatioCalculated }; updateAttributesSafely( @@ -135,16 +135,16 @@ export const CustomImageBlock: React.FC = (props) => { } } setInitialResizeComplete(true); - }, [width, updateAttributes, editorContainer, aspectRatio]); + }, [nodeWidth, updateAttributes, editorContainer, nodeAspectRatio]); // for real time resizing useLayoutEffect(() => { setSize((prevSize) => ({ ...prevSize, - width: ensurePixelString(width), - height: ensurePixelString(height), + width: ensurePixelString(nodeWidth), + height: ensurePixelString(nodeHeight), })); - }, [width, height]); + }, [nodeWidth, nodeHeight]); const handleResize = useCallback( (e: MouseEvent | TouchEvent) => { From a8aac1ab8500c06e262bee5ac2f1fcd9443e42a0 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Mon, 14 Oct 2024 20:33:17 +0530 Subject: [PATCH 03/12] fix: restore only on error --- .../custom-image/components/image-block.tsx | 11 +++-- .../extensions/custom-image/custom-image.ts | 47 ++++++++++++------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index c7008db00ef..a0aaa74721d 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -229,9 +229,14 @@ export const CustomImageBlock: React.FC = (props) => { ref={imageRef} src={displayedImageSrc} onLoad={handleImageLoad} - onError={(e) => { - console.error("Error loading image", e); - setFailedToLoadImage(true); + onError={async (e) => { + try { + await editor?.commands.restoreImage(remoteImageSrc); + imageRef.current.src = remoteImageSrc; + } catch { + setFailedToLoadImage(true); + } + console.log("error while loading image", e); }} width={size.width} className={cn("image-component block rounded-md", { diff --git a/packages/editor/src/core/extensions/custom-image/custom-image.ts b/packages/editor/src/core/extensions/custom-image/custom-image.ts index 939d97668fe..cd889c6c2ae 100644 --- a/packages/editor/src/core/extensions/custom-image/custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/custom-image.ts @@ -22,6 +22,8 @@ declare module "@tiptap/core" { imageComponent: { insertImageComponent: ({ file, pos, event }: InsertImageComponentProps) => ReturnType; uploadImage: (file: File) => () => Promise | undefined; + restoreImage: (src: string) => Promise<() => Promise>; + getImageSource?: (path: string) => () => string; }; } } @@ -36,7 +38,13 @@ export interface UploadImageExtensionStorage { export type UploadEntity = ({ event: "insert" } | { event: "drop"; file: File }) & { hasOpenedFileInputOnce?: boolean }; export const CustomImageExtension = (props: TFileHandler) => { - const { upload, delete: deleteImage, restore: restoreImage } = props; + const { + getAssetSrc, + upload, + delete: deleteImage, + restore, + validation: { maxFileSize }, + } = props; return Image.extend, UploadImageExtensionStorage>({ name: "imageComponent", @@ -78,22 +86,21 @@ export const CustomImageExtension = (props: TFileHandler) => { return ["image-component", mergeAttributes(HTMLAttributes)]; }, - onCreate(this) { - const imageSources = new Set(); - this.editor.state.doc.descendants((node) => { - if (node.type.name === this.name) { - imageSources.add(node.attrs.src); - } - }); - imageSources.forEach(async (src) => { - try { - const assetUrlWithWorkspaceId = new URL(src).pathname.substring(1); - await restoreImage(assetUrlWithWorkspaceId); - } catch (error) { - console.error("Error restoring image: ", error); - } - }); - }, + // onCreate(this) { + // const imageSources = new Set(); + // this.editor.state.doc.descendants((node) => { + // if (node.type.name === this.name) { + // imageSources.add(node.attrs.src); + // } + // }); + // imageSources.forEach(async (src) => { + // try { + // await restore(src); + // } catch (error) { + // console.error("Error restoring image: ", error); + // } + // }); + // }, addKeyboardShortcuts() { return { @@ -105,7 +112,7 @@ export const CustomImageExtension = (props: TFileHandler) => { addProseMirrorPlugins() { return [ TrackImageDeletionPlugin(this.editor, deleteImage, this.name), - TrackImageRestorationPlugin(this.editor, restoreImage, this.name), + // TrackImageRestorationPlugin(this.editor, restore, this.name), ]; }, @@ -166,6 +173,10 @@ export const CustomImageExtension = (props: TFileHandler) => { const fileUrl = await upload(file); return fileUrl; }, + restoreImage: (src: string) => async () => { + await restore(src); + }, + getImageSource: (path: string) => () => getAssetSrc(path), }; }, From 2fab0b52313f00093466c644a98108693488acdd Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Mon, 14 Oct 2024 21:24:14 +0530 Subject: [PATCH 04/12] fix: restore image by handling it inside the image component --- .../custom-image/components/image-block.tsx | 2 +- .../extensions/custom-image/custom-image.ts | 20 +--------- .../src/core/extensions/image/extension.tsx | 37 ++++++++++--------- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 864ff7fdb4e..b812a40dd72 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -59,7 +59,7 @@ export const CustomImageBlock: React.FC = (props) => { src: remoteImageSrc, setEditorContainer, } = props; - const { src: remoteImageSrc, width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs; + const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs; // states const [size, setSize] = useState({ width: ensurePixelString(nodeWidth, "35%"), diff --git a/packages/editor/src/core/extensions/custom-image/custom-image.ts b/packages/editor/src/core/extensions/custom-image/custom-image.ts index e252d39f206..07e718e87d8 100644 --- a/packages/editor/src/core/extensions/custom-image/custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/custom-image.ts @@ -5,7 +5,7 @@ import { v4 as uuidv4 } from "uuid"; // extensions import { CustomImageNode } from "@/extensions/custom-image"; // plugins -import { TrackImageDeletionPlugin, TrackImageRestorationPlugin, isFileValid } from "@/plugins/image"; +import { TrackImageDeletionPlugin, isFileValid } from "@/plugins/image"; // types import { TFileHandler } from "@/types"; // helpers @@ -22,7 +22,7 @@ declare module "@tiptap/core" { imageComponent: { insertImageComponent: ({ file, pos, event }: InsertImageComponentProps) => ReturnType; uploadImage: (file: File) => () => Promise | undefined; - restoreImage: (src: string) => Promise<() => Promise>; + restoreImage: (src: string) => () => Promise; getImageSource?: (path: string) => () => string; }; } @@ -86,22 +86,6 @@ export const CustomImageExtension = (props: TFileHandler) => { return ["image-component", mergeAttributes(HTMLAttributes)]; }, - // onCreate(this) { - // const imageSources = new Set(); - // this.editor.state.doc.descendants((node) => { - // if (node.type.name === this.name) { - // imageSources.add(node.attrs.src); - // } - // }); - // imageSources.forEach(async (src) => { - // try { - // await restore(src); - // } catch (error) { - // console.error("Error restoring image: ", error); - // } - // }); - // }, - addKeyboardShortcuts() { return { ArrowDown: insertEmptyParagraphAtNodeBoundaries("down", this.name), diff --git a/packages/editor/src/core/extensions/image/extension.tsx b/packages/editor/src/core/extensions/image/extension.tsx index e430b88a8d7..83a0cc5798d 100644 --- a/packages/editor/src/core/extensions/image/extension.tsx +++ b/packages/editor/src/core/extensions/image/extension.tsx @@ -13,7 +13,7 @@ export const ImageExtension = (fileHandler: TFileHandler) => { const { delete: deleteImage, getAssetSrc, - restore: restoreImage, + restore, validation: { maxFileSize }, } = fileHandler; @@ -27,25 +27,25 @@ export const ImageExtension = (fileHandler: TFileHandler) => { addProseMirrorPlugins() { return [ TrackImageDeletionPlugin(this.editor, deleteImage, this.name), - TrackImageRestorationPlugin(this.editor, restoreImage, this.name), + // TrackImageRestorationPlugin(this.editor, restoreImage, this.name), ]; }, - onCreate(this) { - const imageSources = new Set(); - this.editor.state.doc.descendants((node) => { - if (node.type.name === this.name) { - imageSources.add(node.attrs.src); - } - }); - imageSources.forEach(async (src) => { - try { - await restoreImage(src); - } catch (error) { - console.error("Error restoring image: ", error); - } - }); - }, + // onCreate(this) { + // const imageSources = new Set(); + // this.editor.state.doc.descendants((node) => { + // if (node.type.name === this.name) { + // imageSources.add(node.attrs.src); + // } + // }); + // imageSources.forEach(async (src) => { + // try { + // await restoreImage(src); + // } catch (error) { + // console.error("Error restoring image: ", error); + // } + // }); + // }, // storage to keep track of image states Map addStorage() { @@ -71,6 +71,9 @@ export const ImageExtension = (fileHandler: TFileHandler) => { addCommands() { return { getImageSource: (path: string) => () => getAssetSrc(path), + restoreImage: (src: string) => async () => { + await restore(src); + }, }; }, From 501ba51ee3145fd1f32f4a49911b390b4da85267 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Tue, 15 Oct 2024 21:07:08 +0530 Subject: [PATCH 05/12] fix: image restoration fixed and aspect ratio added to old images to stop updates on load --- .../custom-image/components/image-block.tsx | 29 +++++++++++++++---- .../custom-image/components/image-node.tsx | 15 +++++----- .../components/image-uploader.tsx | 23 +++++---------- .../extensions/custom-image/custom-image.ts | 5 +--- .../src/core/extensions/image/extension.tsx | 26 ++++------------- .../image/image-extension-without-props.tsx | 3 ++ .../core/extensions/image/read-only-image.tsx | 3 ++ 7 files changed, 51 insertions(+), 53 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index b812a40dd72..7538a97dfbc 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -59,7 +59,7 @@ export const CustomImageBlock: React.FC = (props) => { src: remoteImageSrc, setEditorContainer, } = props; - const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs; + const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs as ImageAttributes; // states const [size, setSize] = useState({ width: ensurePixelString(nodeWidth, "35%"), @@ -72,6 +72,7 @@ export const CustomImageBlock: React.FC = (props) => { const containerRef = useRef(null); const containerRect = useRef(null); const imageRef = useRef(null); + const [onFirstLoadError, setOnFirstLoadError] = useState(false); const updateAttributesSafely = useCallback( (attributes: Partial, errorMessage: string) => { @@ -137,7 +138,7 @@ export const CustomImageBlock: React.FC = (props) => { } } setInitialResizeComplete(true); - }, [nodeWidth, updateAttributes, editorContainer, nodeAspectRatio]); + }, [nodeWidth, updateAttributes, editorContainer, nodeAspectRatio, size.aspectRatio]); // for real time resizing useLayoutEffect(() => { @@ -145,8 +146,9 @@ export const CustomImageBlock: React.FC = (props) => { ...prevSize, width: ensurePixelString(nodeWidth), height: ensurePixelString(nodeHeight), + aspectRatio: nodeAspectRatio, })); - }, [nodeWidth, nodeHeight]); + }, [nodeWidth, nodeHeight, nodeAspectRatio]); const handleResize = useCallback( (e: MouseEvent | TouchEvent) => { @@ -159,7 +161,7 @@ export const CustomImageBlock: React.FC = (props) => { setSize((prevSize) => ({ ...prevSize, width: `${newWidth}px`, height: `${newHeight}px` })); }, - [size] + [size.aspectRatio] ); const handleResizeEnd = useCallback(() => { @@ -182,11 +184,15 @@ export const CustomImageBlock: React.FC = (props) => { window.addEventListener("mousemove", handleResize); window.addEventListener("mouseup", handleResizeEnd); window.addEventListener("mouseleave", handleResizeEnd); + window.addEventListener("touchmove", handleResize, { passive: false }); + window.addEventListener("touchend", handleResizeEnd); return () => { window.removeEventListener("mousemove", handleResize); window.removeEventListener("mouseup", handleResizeEnd); window.removeEventListener("mouseleave", handleResizeEnd); + window.removeEventListener("touchmove", handleResize); + window.removeEventListener("touchend", handleResizeEnd); }; } }, [isResizing, handleResize, handleResizeEnd]); @@ -203,7 +209,7 @@ export const CustomImageBlock: React.FC = (props) => { // show the image loader if the remote image's src or preview image from filesystem is not set yet (while loading the image post upload) (or) // if the initial resize (from 35% width and "auto" height attrs to the actual size in px) is not complete - const showImageLoader = !(remoteImageSrc || imageFromFileSystem) || !initialResizeComplete; + const showImageLoader = !(remoteImageSrc || imageFromFileSystem) || !initialResizeComplete || onFirstLoadError; // show the image utils only if the remote image's (post upload) src is set and the initial resize is complete (but not while we're showing the preview imageFromFileSystem) const showImageUtils = remoteImageSrc && initialResizeComplete; // show the image resizer only if the editor is editable, the remote image's (post upload) src is set and the initial resize is complete (but not while we're showing the preview imageFromFileSystem) @@ -233,12 +239,22 @@ export const CustomImageBlock: React.FC = (props) => { onLoad={handleImageLoad} onError={async (e) => { try { + setOnFirstLoadError(true); + // this is a type error from tiptap, don't remove await until it's fixed await editor?.commands.restoreImage(remoteImageSrc); + console.log( + "imageRef width", + imageRef.current.naturalWidth, + imageRef.current.naturalHeight, + imageRef.current.src.split("/")[10] + ); imageRef.current.src = remoteImageSrc; } catch { setFailedToLoadImage(true); + console.log("Error while loading image", e); + } finally { + setOnFirstLoadError(false); } - console.log("error while loading image", e); }} width={size.width} className={cn("image-component block rounded-md", { @@ -289,6 +305,7 @@ export const CustomImageBlock: React.FC = (props) => { } )} onMouseDown={handleResizeStart} + onTouchStart={handleResizeStart} /> )} diff --git a/packages/editor/src/core/extensions/custom-image/components/image-node.tsx b/packages/editor/src/core/extensions/custom-image/components/image-node.tsx index f743b0a3c1d..56e67f7214d 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-node.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-node.tsx @@ -1,21 +1,23 @@ import { useEffect, useRef, useState } from "react"; -import { Node as ProsemirrorNode } from "@tiptap/pm/model"; -import { Editor, NodeViewWrapper } from "@tiptap/react"; +import { Editor, NodeViewProps, NodeViewWrapper } from "@tiptap/react"; // extensions import { CustomImageBlock, CustomImageUploader, ImageAttributes } from "@/extensions/custom-image"; -export type CustomImageNodeViewProps = { +export type CustomImageComponentProps = { getPos: () => number; editor: Editor; - node: ProsemirrorNode & { + node: NodeViewProps["node"] & { attrs: ImageAttributes; }; updateAttributes: (attrs: Record) => void; selected: boolean; }; +export type CustomImageNodeViewProps = NodeViewProps & CustomImageComponentProps; + export const CustomImageNode = (props: CustomImageNodeViewProps) => { const { getPos, editor, node, updateAttributes, selected } = props; + const { src: remoteImageSrc } = node.attrs; const [isUploaded, setIsUploaded] = useState(false); const [imageFromFileSystem, setImageFromFileSystem] = useState(undefined); @@ -37,14 +39,13 @@ export const CustomImageNode = (props: CustomImageNodeViewProps) => { // the image is already uploaded if the image-component node has src attribute // and we need to remove the blob from our file system useEffect(() => { - const remoteImageSrc = node.attrs.src; if (remoteImageSrc) { setIsUploaded(true); setImageFromFileSystem(undefined); } else { setIsUploaded(false); } - }, [node.attrs.src]); + }, [remoteImageSrc]); return ( @@ -55,7 +56,7 @@ export const CustomImageNode = (props: CustomImageNodeViewProps) => { editorContainer={editorContainer} editor={editor} // @ts-expect-error function not expected here, but will still work - src={editor?.commands?.getImageSource?.(node.attrs.src)} + src={editor?.commands?.getImageSource?.(remoteImageSrc)} getPos={getPos} node={node} setEditorContainer={setEditorContainer} diff --git a/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx b/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx index 67cb4e32927..36f1361ee80 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx @@ -1,27 +1,20 @@ import { ChangeEvent, useCallback, useEffect, useMemo, useRef } from "react"; -import { Node as ProsemirrorNode } from "@tiptap/pm/model"; -import { Editor } from "@tiptap/core"; import { ImageIcon } from "lucide-react"; // helpers import { cn } from "@/helpers/common"; // hooks import { useUploader, useDropZone, uploadFirstImageAndInsertRemaining } from "@/hooks/use-file-upload"; // extensions -import { getImageComponentImageFileMap, ImageAttributes } from "@/extensions/custom-image"; +import { type CustomImageComponentProps, getImageComponentImageFileMap } from "@/extensions/custom-image"; -export const CustomImageUploader = (props: { - editor: Editor; - failedToLoadImage: boolean; - getPos: () => number; - loadImageFromFileSystem: (file: string) => void; +type CustomImageUploaderProps = CustomImageComponentProps & { maxFileSize: number; - node: ProsemirrorNode & { - attrs: ImageAttributes; - }; - selected: boolean; + loadImageFromFileSystem: (file: string) => void; + failedToLoadImage: boolean; setIsUploaded: (isUploaded: boolean) => void; - updateAttributes: (attrs: Record) => void; -}) => { +}; + +export const CustomImageUploader = (props: CustomImageUploaderProps) => { const { editor, failedToLoadImage, @@ -36,8 +29,8 @@ export const CustomImageUploader = (props: { // refs const fileInputRef = useRef(null); const hasTriggeredFilePickerRef = useRef(false); + const { id: imageEntityId } = node.attrs; // derived values - const imageEntityId = node.attrs.id; const imageComponentImageFileMap = useMemo(() => getImageComponentImageFileMap(editor), [editor]); const onUpload = useCallback( diff --git a/packages/editor/src/core/extensions/custom-image/custom-image.ts b/packages/editor/src/core/extensions/custom-image/custom-image.ts index 07e718e87d8..8afc5331d40 100644 --- a/packages/editor/src/core/extensions/custom-image/custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/custom-image.ts @@ -94,10 +94,7 @@ export const CustomImageExtension = (props: TFileHandler) => { }, addProseMirrorPlugins() { - return [ - TrackImageDeletionPlugin(this.editor, deleteImage, this.name), - // TrackImageRestorationPlugin(this.editor, restore, this.name), - ]; + return [TrackImageDeletionPlugin(this.editor, deleteImage, this.name)]; }, addStorage() { diff --git a/packages/editor/src/core/extensions/image/extension.tsx b/packages/editor/src/core/extensions/image/extension.tsx index 83a0cc5798d..6faf96febe1 100644 --- a/packages/editor/src/core/extensions/image/extension.tsx +++ b/packages/editor/src/core/extensions/image/extension.tsx @@ -3,7 +3,7 @@ import { ReactNodeViewRenderer } from "@tiptap/react"; // helpers import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary"; // plugins -import { ImageExtensionStorage, TrackImageDeletionPlugin, TrackImageRestorationPlugin } from "@/plugins/image"; +import { ImageExtensionStorage, TrackImageDeletionPlugin } from "@/plugins/image"; // types import { TFileHandler } from "@/types"; // extensions @@ -25,28 +25,9 @@ export const ImageExtension = (fileHandler: TFileHandler) => { }; }, addProseMirrorPlugins() { - return [ - TrackImageDeletionPlugin(this.editor, deleteImage, this.name), - // TrackImageRestorationPlugin(this.editor, restoreImage, this.name), - ]; + return [TrackImageDeletionPlugin(this.editor, deleteImage, this.name)]; }, - // onCreate(this) { - // const imageSources = new Set(); - // this.editor.state.doc.descendants((node) => { - // if (node.type.name === this.name) { - // imageSources.add(node.attrs.src); - // } - // }); - // imageSources.forEach(async (src) => { - // try { - // await restoreImage(src); - // } catch (error) { - // console.error("Error restoring image: ", error); - // } - // }); - // }, - // storage to keep track of image states Map addStorage() { return { @@ -65,6 +46,9 @@ export const ImageExtension = (fileHandler: TFileHandler) => { height: { default: null, }, + aspectRatio: { + default: null, + }, }; }, diff --git a/packages/editor/src/core/extensions/image/image-extension-without-props.tsx b/packages/editor/src/core/extensions/image/image-extension-without-props.tsx index 0d505000c7e..bb6c5b4ad81 100644 --- a/packages/editor/src/core/extensions/image/image-extension-without-props.tsx +++ b/packages/editor/src/core/extensions/image/image-extension-without-props.tsx @@ -11,6 +11,9 @@ export const ImageExtensionWithoutProps = () => height: { default: null, }, + aspectRatio: { + default: null, + }, }; }, }); diff --git a/packages/editor/src/core/extensions/image/read-only-image.tsx b/packages/editor/src/core/extensions/image/read-only-image.tsx index 7ba961cdb67..c884a43ee72 100644 --- a/packages/editor/src/core/extensions/image/read-only-image.tsx +++ b/packages/editor/src/core/extensions/image/read-only-image.tsx @@ -18,6 +18,9 @@ export const ReadOnlyImageExtension = (props: Pick) height: { default: null, }, + aspectRatio: { + default: null, + }, }; }, From 43e0d100bb43d9d25c4cae1a901892ae418ceefa Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Thu, 17 Oct 2024 18:56:59 +0530 Subject: [PATCH 06/12] fix: added back restoring logic for public images --- .../custom-image/components/image-block.tsx | 13 ++++--- .../extensions/custom-image/custom-image.ts | 32 ++++++++++++++--- .../src/core/extensions/image/extension.tsx | 35 +++++++++++++++---- .../src/core/plugins/image/restore-image.ts | 3 ++ 4 files changed, 64 insertions(+), 19 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 7538a97dfbc..10a894e65f9 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -238,16 +238,15 @@ export const CustomImageBlock: React.FC = (props) => { src={displayedImageSrc} onLoad={handleImageLoad} onError={async (e) => { + // for old image extension this command doesn't exist + if (!editor?.commands.restoreImage) { + setFailedToLoadImage(true); + } + try { setOnFirstLoadError(true); // this is a type error from tiptap, don't remove await until it's fixed - await editor?.commands.restoreImage(remoteImageSrc); - console.log( - "imageRef width", - imageRef.current.naturalWidth, - imageRef.current.naturalHeight, - imageRef.current.src.split("/")[10] - ); + await editor?.commands.restoreImage?.(remoteImageSrc); imageRef.current.src = remoteImageSrc; } catch { setFailedToLoadImage(true); diff --git a/packages/editor/src/core/extensions/custom-image/custom-image.ts b/packages/editor/src/core/extensions/custom-image/custom-image.ts index 8afc5331d40..98b3ee14c5a 100644 --- a/packages/editor/src/core/extensions/custom-image/custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/custom-image.ts @@ -5,7 +5,7 @@ import { v4 as uuidv4 } from "uuid"; // extensions import { CustomImageNode } from "@/extensions/custom-image"; // plugins -import { TrackImageDeletionPlugin, isFileValid } from "@/plugins/image"; +import { TrackImageDeletionPlugin, TrackImageRestorationPlugin, isFileValid } from "@/plugins/image"; // types import { TFileHandler } from "@/types"; // helpers @@ -41,8 +41,8 @@ export const CustomImageExtension = (props: TFileHandler) => { const { getAssetSrc, upload, - delete: deleteImage, - restore, + delete: deleteImageFn, + restore: restoreImageFn, validation: { maxFileSize }, } = props; @@ -94,7 +94,29 @@ export const CustomImageExtension = (props: TFileHandler) => { }, addProseMirrorPlugins() { - return [TrackImageDeletionPlugin(this.editor, deleteImage, this.name)]; + return [ + TrackImageDeletionPlugin(this.editor, deleteImageFn, this.name), + TrackImageRestorationPlugin(this.editor, restoreImageFn, this.name), + ]; + }, + + onCreate(this) { + const imageSources = new Set(); + this.editor.state.doc.descendants((node) => { + if (node.type.name === this.name) { + if (!node.attrs.src.startsWith("http")) { + return; + } + imageSources.add(node.attrs.src); + } + }); + imageSources.forEach(async (src) => { + try { + await restoreImageFn(src); + } catch (error) { + console.error("Error restoring image: ", error); + } + }); }, addStorage() { @@ -162,7 +184,7 @@ export const CustomImageExtension = (props: TFileHandler) => { return fileUrl; }, restoreImage: (src: string) => async () => { - await restore(src); + await restoreImageFn(src); }, getImageSource: (path: string) => () => getAssetSrc(path), }; diff --git a/packages/editor/src/core/extensions/image/extension.tsx b/packages/editor/src/core/extensions/image/extension.tsx index 6faf96febe1..12dcdf08752 100644 --- a/packages/editor/src/core/extensions/image/extension.tsx +++ b/packages/editor/src/core/extensions/image/extension.tsx @@ -3,7 +3,7 @@ import { ReactNodeViewRenderer } from "@tiptap/react"; // helpers import { insertEmptyParagraphAtNodeBoundaries } from "@/helpers/insert-empty-paragraph-at-node-boundary"; // plugins -import { ImageExtensionStorage, TrackImageDeletionPlugin } from "@/plugins/image"; +import { ImageExtensionStorage, TrackImageDeletionPlugin, TrackImageRestorationPlugin } from "@/plugins/image"; // types import { TFileHandler } from "@/types"; // extensions @@ -11,9 +11,9 @@ import { CustomImageNode } from "@/extensions"; export const ImageExtension = (fileHandler: TFileHandler) => { const { - delete: deleteImage, getAssetSrc, - restore, + delete: deleteImageFn, + restore: restoreImageFn, validation: { maxFileSize }, } = fileHandler; @@ -24,8 +24,32 @@ export const ImageExtension = (fileHandler: TFileHandler) => { ArrowUp: insertEmptyParagraphAtNodeBoundaries("up", this.name), }; }, + addProseMirrorPlugins() { - return [TrackImageDeletionPlugin(this.editor, deleteImage, this.name)]; + return [ + TrackImageDeletionPlugin(this.editor, deleteImageFn, this.name), + TrackImageRestorationPlugin(this.editor, restoreImageFn, this.name), + ]; + }, + + onCreate(this) { + const imageSources = new Set(); + this.editor.state.doc.descendants((node) => { + if (node.type.name === this.name) { + console.log("node", node.attrs.src.startsWith("http")); + if (!node.attrs.src.startsWith("http")) { + return; + } + imageSources.add(node.attrs.src); + } + }); + imageSources.forEach(async (src) => { + try { + await restoreImageFn(src); + } catch (error) { + console.error("Error restoring image: ", error); + } + }); }, // storage to keep track of image states Map @@ -55,9 +79,6 @@ export const ImageExtension = (fileHandler: TFileHandler) => { addCommands() { return { getImageSource: (path: string) => () => getAssetSrc(path), - restoreImage: (src: string) => async () => { - await restore(src); - }, }; }, diff --git a/packages/editor/src/core/plugins/image/restore-image.ts b/packages/editor/src/core/plugins/image/restore-image.ts index 4d7279fffcb..94648469177 100644 --- a/packages/editor/src/core/plugins/image/restore-image.ts +++ b/packages/editor/src/core/plugins/image/restore-image.ts @@ -25,6 +25,9 @@ export const TrackImageRestorationPlugin = (editor: Editor, restoreImage: Restor if (node.type.name !== nodeType) return; if (pos < 0 || pos > newState.doc.content.size) return; if (oldImageSources.has(node.attrs.src)) return; + // if the src is just a id (private bucket), then we don't need to handle restore from here but + // only while it fails to load + if (!node.attrs.src.startsWith("http")) return; addedImages.push(node as ImageNode); }); From 85cc0058f88f566b6b5db7fe44113eb6bcd05a2a Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Fri, 18 Oct 2024 13:26:53 +0530 Subject: [PATCH 07/12] fix: add conditions --- .../editor/src/core/extensions/custom-image/custom-image.ts | 2 +- packages/editor/src/core/extensions/image/extension.tsx | 4 ++-- packages/editor/src/core/plugins/image/restore-image.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/custom-image.ts b/packages/editor/src/core/extensions/custom-image/custom-image.ts index 98b3ee14c5a..5e6a77aded5 100644 --- a/packages/editor/src/core/extensions/custom-image/custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/custom-image.ts @@ -104,7 +104,7 @@ export const CustomImageExtension = (props: TFileHandler) => { const imageSources = new Set(); this.editor.state.doc.descendants((node) => { if (node.type.name === this.name) { - if (!node.attrs.src.startsWith("http")) { + if (!node.attrs.src?.startsWith("http")) { return; } imageSources.add(node.attrs.src); diff --git a/packages/editor/src/core/extensions/image/extension.tsx b/packages/editor/src/core/extensions/image/extension.tsx index 12dcdf08752..36219119f64 100644 --- a/packages/editor/src/core/extensions/image/extension.tsx +++ b/packages/editor/src/core/extensions/image/extension.tsx @@ -36,8 +36,8 @@ export const ImageExtension = (fileHandler: TFileHandler) => { const imageSources = new Set(); this.editor.state.doc.descendants((node) => { if (node.type.name === this.name) { - console.log("node", node.attrs.src.startsWith("http")); - if (!node.attrs.src.startsWith("http")) { + console.log("node", node.attrs.src?.startsWith("http")); + if (!node.attrs.src?.startsWith("http")) { return; } imageSources.add(node.attrs.src); diff --git a/packages/editor/src/core/plugins/image/restore-image.ts b/packages/editor/src/core/plugins/image/restore-image.ts index 94648469177..4eecf01d7e2 100644 --- a/packages/editor/src/core/plugins/image/restore-image.ts +++ b/packages/editor/src/core/plugins/image/restore-image.ts @@ -27,7 +27,7 @@ export const TrackImageRestorationPlugin = (editor: Editor, restoreImage: Restor if (oldImageSources.has(node.attrs.src)) return; // if the src is just a id (private bucket), then we don't need to handle restore from here but // only while it fails to load - if (!node.attrs.src.startsWith("http")) return; + if (!node.attrs.src?.startsWith("http")) return; addedImages.push(node as ImageNode); }); From 028443e68962ccac7f005b0291fddd470e81a714 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Fri, 18 Oct 2024 16:02:04 +0530 Subject: [PATCH 08/12] fix: image attributes types --- .../src/core/extensions/custom-image/components/image-block.tsx | 2 +- .../src/core/extensions/custom-image/components/image-node.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 10a894e65f9..3478bbb0162 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -59,7 +59,7 @@ export const CustomImageBlock: React.FC = (props) => { src: remoteImageSrc, setEditorContainer, } = props; - const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs as ImageAttributes; + const { width: nodeWidth, height: nodeHeight, aspectRatio: nodeAspectRatio } = node.attrs; // states const [size, setSize] = useState({ width: ensurePixelString(nodeWidth, "35%"), diff --git a/packages/editor/src/core/extensions/custom-image/components/image-node.tsx b/packages/editor/src/core/extensions/custom-image/components/image-node.tsx index 56e67f7214d..bdb8280c5b4 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-node.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-node.tsx @@ -9,7 +9,7 @@ export type CustomImageComponentProps = { node: NodeViewProps["node"] & { attrs: ImageAttributes; }; - updateAttributes: (attrs: Record) => void; + updateAttributes: (attrs: ImageAttributes) => void; selected: boolean; }; From dd7e726e9abc8c2c0075c53a9d32b71751338cb6 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Fri, 18 Oct 2024 16:13:31 +0530 Subject: [PATCH 09/12] fix: return for old images --- .../core/extensions/custom-image/components/image-block.tsx | 3 ++- packages/editor/src/core/extensions/image/extension.tsx | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 3478bbb0162..5c9518dbc6d 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -138,7 +138,7 @@ export const CustomImageBlock: React.FC = (props) => { } } setInitialResizeComplete(true); - }, [nodeWidth, updateAttributes, editorContainer, nodeAspectRatio, size.aspectRatio]); + }, [nodeWidth, updateAttributes, editorContainer, nodeAspectRatio]); // for real time resizing useLayoutEffect(() => { @@ -241,6 +241,7 @@ export const CustomImageBlock: React.FC = (props) => { // for old image extension this command doesn't exist if (!editor?.commands.restoreImage) { setFailedToLoadImage(true); + return; } try { diff --git a/packages/editor/src/core/extensions/image/extension.tsx b/packages/editor/src/core/extensions/image/extension.tsx index 36219119f64..75e7298aeb6 100644 --- a/packages/editor/src/core/extensions/image/extension.tsx +++ b/packages/editor/src/core/extensions/image/extension.tsx @@ -36,7 +36,6 @@ export const ImageExtension = (fileHandler: TFileHandler) => { const imageSources = new Set(); this.editor.state.doc.descendants((node) => { if (node.type.name === this.name) { - console.log("node", node.attrs.src?.startsWith("http")); if (!node.attrs.src?.startsWith("http")) { return; } From beaaefd2b898440a4c9335b7ad0a47d5c93970bf Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Fri, 18 Oct 2024 16:18:16 +0530 Subject: [PATCH 10/12] fix: remove passive false --- .../core/extensions/custom-image/components/image-block.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 5c9518dbc6d..5133413dbda 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -184,7 +184,7 @@ export const CustomImageBlock: React.FC = (props) => { window.addEventListener("mousemove", handleResize); window.addEventListener("mouseup", handleResizeEnd); window.addEventListener("mouseleave", handleResizeEnd); - window.addEventListener("touchmove", handleResize, { passive: false }); + window.addEventListener("touchmove", handleResize); window.addEventListener("touchend", handleResizeEnd); return () => { @@ -251,7 +251,7 @@ export const CustomImageBlock: React.FC = (props) => { imageRef.current.src = remoteImageSrc; } catch { setFailedToLoadImage(true); - console.log("Error while loading image", e); + console.error("Error while loading image", e); } finally { setOnFirstLoadError(false); } From 4325115e7cf08983ea244b9f9849f5aad96d83e3 Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Fri, 18 Oct 2024 16:36:22 +0530 Subject: [PATCH 11/12] fix: eslint fixes --- .../editor/src/core/extensions/custom-image/custom-image.ts | 5 ++--- packages/editor/src/core/extensions/image/extension.tsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/custom-image.ts b/packages/editor/src/core/extensions/custom-image/custom-image.ts index 5e6a77aded5..2c5e2bb8d43 100644 --- a/packages/editor/src/core/extensions/custom-image/custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/custom-image.ts @@ -104,9 +104,8 @@ export const CustomImageExtension = (props: TFileHandler) => { const imageSources = new Set(); this.editor.state.doc.descendants((node) => { if (node.type.name === this.name) { - if (!node.attrs.src?.startsWith("http")) { - return; - } + if (!node.attrs.src?.startsWith("http")) return; + imageSources.add(node.attrs.src); } }); diff --git a/packages/editor/src/core/extensions/image/extension.tsx b/packages/editor/src/core/extensions/image/extension.tsx index 75e7298aeb6..f7666bfe24b 100644 --- a/packages/editor/src/core/extensions/image/extension.tsx +++ b/packages/editor/src/core/extensions/image/extension.tsx @@ -36,9 +36,8 @@ export const ImageExtension = (fileHandler: TFileHandler) => { const imageSources = new Set(); this.editor.state.doc.descendants((node) => { if (node.type.name === this.name) { - if (!node.attrs.src?.startsWith("http")) { - return; - } + if (!node.attrs.src?.startsWith("http")) return; + imageSources.add(node.attrs.src); } }); From 7d54131bac7a20db262a72f2ac3490310ee8a26f Mon Sep 17 00:00:00 2001 From: Palanikannan M Date: Fri, 18 Oct 2024 17:03:49 +0530 Subject: [PATCH 12/12] fix: stopping infinite loading scenarios while restoring from error --- .../custom-image/components/image-block.tsx | 15 +++++++++------ .../editor/src/core/extensions/extensions.tsx | 5 ++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 5133413dbda..b89633f4e4d 100644 --- a/packages/editor/src/core/extensions/custom-image/components/image-block.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -72,7 +72,8 @@ export const CustomImageBlock: React.FC = (props) => { const containerRef = useRef(null); const containerRect = useRef(null); const imageRef = useRef(null); - const [onFirstLoadError, setOnFirstLoadError] = useState(false); + const [hasErroredOnFirstLoad, setHasErroredOnFirstLoad] = useState(false); + const [hasTriedRestoringImageOnce, setHasTriedRestoringImageOnce] = useState(false); const updateAttributesSafely = useCallback( (attributes: Partial, errorMessage: string) => { @@ -209,7 +210,7 @@ export const CustomImageBlock: React.FC = (props) => { // show the image loader if the remote image's src or preview image from filesystem is not set yet (while loading the image post upload) (or) // if the initial resize (from 35% width and "auto" height attrs to the actual size in px) is not complete - const showImageLoader = !(remoteImageSrc || imageFromFileSystem) || !initialResizeComplete || onFirstLoadError; + const showImageLoader = !(remoteImageSrc || imageFromFileSystem) || !initialResizeComplete || hasErroredOnFirstLoad; // show the image utils only if the remote image's (post upload) src is set and the initial resize is complete (but not while we're showing the preview imageFromFileSystem) const showImageUtils = remoteImageSrc && initialResizeComplete; // show the image resizer only if the editor is editable, the remote image's (post upload) src is set and the initial resize is complete (but not while we're showing the preview imageFromFileSystem) @@ -238,22 +239,24 @@ export const CustomImageBlock: React.FC = (props) => { src={displayedImageSrc} onLoad={handleImageLoad} onError={async (e) => { - // for old image extension this command doesn't exist - if (!editor?.commands.restoreImage) { + // for old image extension this command doesn't exist or if the image failed to load for the first time + if (!editor?.commands.restoreImage || hasTriedRestoringImageOnce) { setFailedToLoadImage(true); return; } try { - setOnFirstLoadError(true); + setHasErroredOnFirstLoad(true); // this is a type error from tiptap, don't remove await until it's fixed await editor?.commands.restoreImage?.(remoteImageSrc); imageRef.current.src = remoteImageSrc; } catch { + // if the image failed to even restore, then show the error state setFailedToLoadImage(true); console.error("Error while loading image", e); } finally { - setOnFirstLoadError(false); + setHasErroredOnFirstLoad(false); + setHasTriedRestoringImageOnce(true); } }} width={size.width} diff --git a/packages/editor/src/core/extensions/extensions.tsx b/packages/editor/src/core/extensions/extensions.tsx index f4edfdb5c9b..f74a814c11f 100644 --- a/packages/editor/src/core/extensions/extensions.tsx +++ b/packages/editor/src/core/extensions/extensions.tsx @@ -140,7 +140,10 @@ export const CoreEditorExtensions = (args: TArguments) => { if (editor.storage.imageComponent.uploadInProgress) return ""; const shouldHidePlaceholder = - editor.isActive("table") || editor.isActive("codeBlock") || editor.isActive("image"); + editor.isActive("table") || + editor.isActive("codeBlock") || + editor.isActive("image") || + editor.isActive("imageComponent"); if (shouldHidePlaceholder) return "";