import type { Editor } from "@tiptap/react" export type UploadedEditorImage = { assetId: string provider: string storageKey: string filename?: string } export type UploadedEditorImageMap = Map export function removeEditorImageByTitle(editor: Editor, title: string) { const { state } = editor let targetPos: number | null = null state.doc.descendants((node, pos) => { if (node.type.name === "image" && node.attrs.title === title) { targetPos = pos return false } return true }) if (targetPos === null) { return } editor.chain().focus().deleteRange({ from: targetPos, to: targetPos + 1 }).run() } export function markEditorImageUploadedByTitle( editor: Editor, title: string, uploaded: UploadedEditorImage, uploadedImages: UploadedEditorImageMap ) { uploadedImages.set(title, uploaded) const image = findEditorImageElementByTitle(editor, title) if (!image) { return } image.setAttribute("data-asset-id", uploaded.assetId) image.setAttribute("data-provider", uploaded.provider) image.setAttribute("data-storage-key", uploaded.storageKey) image.setAttribute("alt", uploaded.filename || image.getAttribute("alt") || "image") image.classList.remove("cs-agent-editor-image-uploading") image.removeAttribute("data-uploading") image.removeAttribute("title") } export function setEditorImageUploadingByTitle(editor: Editor, title: string) { const image = findEditorImageElementByTitle(editor, title) if (!image) { return } image.classList.add("cs-agent-editor-image-uploading") image.setAttribute("data-uploading", "true") } export function buildSendableEditorHTML( html: string, uploadedImages?: UploadedEditorImageMap ) { if (typeof document === "undefined" || !html.includes("]*\btitle=(["'])uploading-[^"']+\1/i.test(html) } const template = document.createElement("template") template.innerHTML = html return Array.from(template.content.querySelectorAll("img")).some((image) => isUnfinishedUploadingImage(image, uploadedImages) ) } export function revokeEditorObjectUrl(urls: Set, objectUrl: string) { if (!urls.delete(objectUrl)) { return } URL.revokeObjectURL(objectUrl) } export function revokeEditorObjectUrls(urls: Set) { for (const objectUrl of urls) { URL.revokeObjectURL(objectUrl) } urls.clear() } function findEditorImageElementByTitle(editor: Editor, title: string) { const escapedTitle = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(title) : title.replace(/["\\]/g, "\\$&") return editor.view.dom.querySelector( `img[title="${escapedTitle}"]` ) } function isUnfinishedUploadingImage( image: HTMLImageElement, uploadedImages?: UploadedEditorImageMap ) { const title = image.getAttribute("title") ?? "" return title.startsWith("uploading-") && !uploadedImages?.has(title) }