"use client"; import { useEffect, useId, useRef, useState } from "react"; // TinyMCE community build, loaded from the public jsDelivr CDN. `no-api-key` // is the documented sentinel for the free self-hosted/CDN build and silences // the "register for an API key" notification. const TINYMCE_SRC = "https://cdn.jsdelivr.net/npm/tinymce@6/tinymce.min.js"; type TinyMCEGlobal = { init: (opts: Record) => Promise; remove: (selector: string) => void; }; declare global { interface Window { tinymce?: TinyMCEGlobal; } } let loaderPromise: Promise | null = null; /** Inject the TinyMCE script once; resolves null if it fails to load. */ function loadTinyMce(): Promise { if (typeof window === "undefined") return Promise.resolve(null); if (window.tinymce) return Promise.resolve(window.tinymce); if (loaderPromise) return loaderPromise; loaderPromise = new Promise((resolve) => { const existing = document.querySelector( "script[data-tinymce]", ); const finish = () => resolve(window.tinymce ?? null); if (existing) { existing.addEventListener("load", finish, { once: true }); existing.addEventListener("error", () => resolve(null), { once: true }); if (window.tinymce) resolve(window.tinymce); return; } const script = document.createElement("script"); script.src = TINYMCE_SRC; script.async = true; script.referrerPolicy = "origin"; script.dataset.tinymce = "1"; script.addEventListener("load", finish, { once: true }); script.addEventListener("error", () => resolve(null), { once: true }); document.head.appendChild(script); }); return loaderPromise; } /** * WYSIWYG editor over a plain