+ {/* Backdrop */}
+
+
+ {/* Image Container */}
+
+

100
+ ? isMousePanning
+ ? "grabbing"
+ : "grab"
+ : "default",
+ transition:
+ isPanning || isPinching || isAnimatingPan
+ ? "none"
+ : "transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)",
+ }}
+ onMouseDown={handlePanStart}
+ draggable={false}
+ />
+
+
+ {/* Caption */}
+ {currentImage.alt && (
+
+ {currentImage.alt}
+
+ )}
+
+ {/* Controls bar */}
+
e.stopPropagation()}
+ onTouchMove={(e) => e.stopPropagation()}
+ onTouchEnd={(e) => e.stopPropagation()}
+ onMouseDown={(e) => e.stopPropagation()}
+ onWheel={(e) => e.stopPropagation()}
+ >
+ {/* Navigation controls */}
+
+
+
+
+
+
+ {currentIndex + 1}/{images.length}
+
+
+
+
+
+
+
+ {/* Zoom controls */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Action controls */}
+
+
+
+
+
+
+
+
+
+
+ {/* Close button */}
+
+
+
+
+
+
+
+ );
+};
+
+export default ImageViewerModal;
diff --git a/packages/frappe-ui-react/src/components/textEditor/extensions/image-viewer/index.ts b/packages/frappe-ui-react/src/components/textEditor/extensions/image-viewer/index.ts
new file mode 100644
index 00000000..21dfb73f
--- /dev/null
+++ b/packages/frappe-ui-react/src/components/textEditor/extensions/image-viewer/index.ts
@@ -0,0 +1 @@
+export { default as ImageViewerExtension } from "./image-viewer-extension";
diff --git a/packages/frappe-ui-react/src/components/textEditor/extensions/image/image-extension.ts b/packages/frappe-ui-react/src/components/textEditor/extensions/image/image-extension.ts
new file mode 100644
index 00000000..99dfdce7
--- /dev/null
+++ b/packages/frappe-ui-react/src/components/textEditor/extensions/image/image-extension.ts
@@ -0,0 +1,602 @@
+/**
+ * External dependencies.
+ */
+import {
+ Node as NodeExtension,
+ nodeInputRule,
+ mergeAttributes,
+} from "@tiptap/core";
+import { ReactNodeViewRenderer } from "@tiptap/react";
+import { Plugin, Selection, Transaction, EditorState } from "@tiptap/pm/state";
+import { EditorView } from "@tiptap/pm/view";
+
+/**
+ * Internal dependencies.
+ */
+import ImageNodeView from "./imageNodeView";
+import { Node } from "@tiptap/pm/model";
+import fileToBase64 from "../../../../utils/fileToBase64";
+import { UploadedFile } from "../../types";
+
+export interface ImageExtensionOptions {
+ /**
+ * Function to handle image uploads
+ * @default null
+ */
+ uploadFunction: ((file: File) => Promise