// Charger la configuration globale de l'API
const script = document.createElement("script");
// script.src = "/wp-content/plugins/ai-builder/config.js";
document.head.appendChild(script);
// Initialiser Sentry pour le widget si disponible
(function initSentry() {
const SENTRY_SRC = "https://js-de.sentry-cdn.com/a9e1c731a49a9ab9736399318271e322.min.js";
if (window.__aiBuilderSentry) {
return;
}
try {
const sentryScript = document.createElement("script");
sentryScript.src = SENTRY_SRC;
sentryScript.crossOrigin = "anonymous";
sentryScript.onload = () => {
window.__aiBuilderSentry = true;
console.info("AI Builder: Sentry loaded");
};
sentryScript.onerror = () => {
console.warn("AI Builder: Impossible de charger Sentry");
};
document.head.appendChild(sentryScript);
} catch (error) {
console.warn("AI Builder: Erreur lors de l'initialisation de Sentry", error);
}
})();
// Récupération des variables injectées par wp_localize_script (admin)
if (typeof window.aiBuilderVars !== "undefined") {
// Forcer l'utilisation d'une URL relative pour éviter les erreurs CORS
window.ajaxurl = '/wp-admin/admin-ajax.php';
window.aiBuilderNonce = window.aiBuilderVars.nonce;
} else {
// Fallback pour éviter les erreurs CORS
window.ajaxurl = '/wp-admin/admin-ajax.php';
window.aiBuilderNonce = '';
}
document.addEventListener("DOMContentLoaded", () => {
// Charger le CSS des témoignages
const style = document.createElement("link");
style.rel = "stylesheet";
style.href = "/wp-content/plugins/ai-builder/assets/css/cards.css";
document.head.appendChild(style);
document.body.insertAdjacentHTML(
"beforeend",
`
🤖 AI Builder
`
);
const toggle = document.getElementById("chat-toggle");
const box = document.getElementById("chat-box");
const messages = document.getElementById("chat-messages");
const input = document.querySelector("#chat-input textarea");
const sendBtn = document.querySelector("#chat-input button");
const undoBtn = document.getElementById("chat-undo");
let chatHistory = [];
function getChatStorageKey() {
const prefix = "aiBuilderChatHistory";
const postId =
wp?.data?.select("core/editor")?.getCurrentPostId?.() ||
new URL(window.location.href, window.location.origin).searchParams.get("post") ||
new URL(window.location.href, window.location.origin).searchParams.get("postId") ||
new URL(window.location.href, window.location.origin).searchParams.get("p");
if (postId) {
return `${prefix}_post_${postId}`;
}
const patternName = getPatternName();
if (patternName) {
return `${prefix}_pattern_${patternName}`;
}
return `${prefix}_${window.location.pathname}`;
}
// Fonction pour migrer l'historique de post-new.php vers l'ID du post
function migrateChatHistoryFromNewPost() {
if (!window.localStorage) return;
const prefix = "aiBuilderChatHistory";
const newPostKey = `${prefix}_/wp-admin/post-new.php`;
const currentKey = getChatStorageKey();
// Si on est déjà sur la clé post-new.php, pas besoin de migrer
if (currentKey === newPostKey) return;
// Si la clé actuelle est basée sur un ID de post, vérifier s'il y a un historique à migrer
if (currentKey.startsWith(`${prefix}_post_`)) {
try {
const oldHistory = window.localStorage.getItem(newPostKey);
if (oldHistory) {
const parsedHistory = JSON.parse(oldHistory);
if (Array.isArray(parsedHistory) && parsedHistory.length > 0) {
// Fusionner avec l'historique existant (s'il y en a un)
const existingHistory = chatHistory.length > 0 ? chatHistory : [];
chatHistory = [...existingHistory, ...parsedHistory];
// Sauvegarder avec la nouvelle clé
window.localStorage.setItem(currentKey, JSON.stringify(chatHistory));
// Supprimer l'ancienne clé
window.localStorage.removeItem(newPostKey);
console.log("AI Builder: Chat history migrated from post-new.php to post ID");
// Re-rendre l'historique
renderChatHistory();
}
}
} catch (error) {
console.warn("AI Builder: Error migrating chat history", error);
}
}
}
// Éléments CSS
const cssEditButton = document.getElementById("css-edit-button");
const cssModal = document.getElementById("css-modal");
const cssEditor = document.getElementById("css-editor");
const cssSaveBtn = document.getElementById("css-save");
const cssCancelBtn = document.getElementById("css-cancel");
const cssModalClose = document.getElementById("css-modal-close");
// Éléments des onglets
const cssTabPage = document.getElementById("css-tab-page");
const cssTabBlocks = document.getElementById("css-tab-blocks");
toggle.onclick = () => {
const isOpening = box.style.display !== "flex";
box.style.display = box.style.display === "flex" ? "none" : "flex";
box.style.flexDirection = "column";
// Scroller vers le bas quand on ouvre le chat
if (isOpening && messages) {
// Petit délai pour s'assurer que le DOM est mis à jour
setTimeout(() => {
messages.scrollTop = messages.scrollHeight;
}, 100);
}
};
// Logique pour la modale CSS
let currentCSSContent = "";
let currentActiveTab = "page";
// Initialiser les variables CSS globales
window.aiBuilderPageCSS = window.aiBuilderPageCSS || "";
window.aiBuilderBlockCSS = window.aiBuilderBlockCSS || "";
function saveChatHistory() {
if (!window.localStorage) return;
try {
const key = getChatStorageKey();
window.localStorage.setItem(key, JSON.stringify(chatHistory));
} catch (error) {
console.warn("AI Builder: Unable to persist chat history", error);
}
}
function loadChatHistoryFromStorage() {
if (!window.localStorage) return;
try {
const key = getChatStorageKey();
console.log("Loading chat history from storage:", key);
const storedHistory = window.localStorage.getItem(key);
if (!storedHistory) return;
const parsedHistory = JSON.parse(storedHistory);
if (Array.isArray(parsedHistory)) {
chatHistory = parsedHistory;
}
} catch (error) {
console.warn("AI Builder: Unable to read chat history", error);
}
}
function renderChatHistory() {
if (!messages) return;
messages.innerHTML = "";
chatHistory.forEach(({ type, message }) => {
addMessage(message, type, { persist: false });
});
// Scroller vers le bas après avoir rendu l'historique
if (chatHistory.length > 0) {
setTimeout(() => {
messages.scrollTop = messages.scrollHeight;
}, 50);
}
}
function getConversationHistoryString(limit = 4) {
if (!chatHistory.length) return "";
const recentMessages = chatHistory.slice(-limit);
const segments = recentMessages.map(({ type, message }) => {
const label = type === "user" ? "User question" : "AI Response";
return `${label} : ${message}`;
});
return segments.join(". ") + (segments.length ? "." : "");
}
// Fonction pour ajouter des messages dans le chat
function addMessage(message, type = "assistant", options = {}) {
const { persist = true } = options;
if (!messages) return;
const messageDiv = document.createElement("div");
messageDiv.className = type === "assistant" ? "ai-message" : "user-message";
if (type === "assistant") {
messageDiv.innerHTML = `🤖 ${message}`;
} else {
messageDiv.innerHTML = `👤 ${message}`;
}
messages.appendChild(messageDiv);
messages.scrollTop = messages.scrollHeight;
if (persist) {
chatHistory.push({ type, message });
saveChatHistory();
}
}
loadChatHistoryFromStorage();
renderChatHistory();
// Migrer l'historique de post-new.php vers l'ID du post si nécessaire
migrateChatHistoryFromNewPost();
// Surveiller les changements d'ID de post (pour les drafts créés après le chargement)
let lastKnownPostId = null;
let lastKnownKey = getChatStorageKey();
function checkPostIdChange() {
try {
const currentPostId = wp?.data?.select("core/editor")?.getCurrentPostId?.();
const currentKey = getChatStorageKey();
if (currentPostId && currentPostId !== lastKnownPostId) {
// L'ID a changé, mettre à jour la clé de stockage
if (currentKey !== lastKnownKey && window.localStorage) {
// Migrer l'historique vers la nouvelle clé
const oldHistory = window.localStorage.getItem(lastKnownKey);
if (oldHistory) {
try {
const parsedHistory = JSON.parse(oldHistory);
if (Array.isArray(parsedHistory) && parsedHistory.length > 0) {
chatHistory = parsedHistory;
window.localStorage.setItem(currentKey, oldHistory);
// Ne pas supprimer l'ancienne clé immédiatement, au cas où
console.log("AI Builder: Chat history migrated to new post ID:", currentPostId);
renderChatHistory();
}
} catch (e) {
console.warn("AI Builder: Error migrating history on ID change", e);
}
}
}
lastKnownPostId = currentPostId;
lastKnownKey = currentKey;
} else if (currentKey !== lastKnownKey) {
// La clé a changé même si l'ID n'a pas changé (changement d'URL)
lastKnownKey = currentKey;
// Recharger l'historique avec la nouvelle clé
loadChatHistoryFromStorage();
renderChatHistory();
migrateChatHistoryFromNewPost();
}
} catch (e) {
// Ignorer les erreurs si wp.data n'est pas encore disponible
}
}
// Vérifier l'ID initial
setTimeout(() => {
try {
lastKnownPostId = wp?.data?.select("core/editor")?.getCurrentPostId?.();
} catch (e) { }
}, 1000);
// Surveiller les changements d'ID toutes les 2 secondes
setInterval(checkPostIdChange, 2000);
// Ouvrir la modale CSS
cssEditButton.onclick = () => {
cssModal.style.display = "flex";
// Charger le CSS de page par défaut
loadCSSForTab("page");
cssEditor.focus();
};
// Fermer la modale CSS
function closeCSSModal() {
cssModal.style.display = "none";
}
// Fonction pour charger le CSS selon l'onglet sélectionné
function loadCSSForTab(tabType) {
currentActiveTab = tabType;
// Mettre à jour les onglets actifs
document
.querySelectorAll(".css-tab")
.forEach((tab) => tab.classList.remove("active"));
document.getElementById(`css-tab-${tabType}`).classList.add("active");
// Charger le bon CSS
switch (tabType) {
case "page":
cssEditor.value = window.aiBuilderPageCSS || "";
console.log(
"Loading page CSS:",
(window.aiBuilderPageCSS || "").length,
"chars"
);
break;
case "blocks":
cssEditor.value = window.aiBuilderBlockCSS || "";
console.log(
"Loading blocks CSS:",
(window.aiBuilderBlockCSS || "").length,
"chars"
);
break;
}
}
// Gestion des onglets
cssTabPage.onclick = () => loadCSSForTab("page");
cssTabBlocks.onclick = () => loadCSSForTab("blocks");
cssModalClose.onclick = closeCSSModal;
cssCancelBtn.onclick = closeCSSModal;
// Sauvegarder le CSS
cssSaveBtn.onclick = async () => {
const newCSSContent = cssEditor.value;
// Déterminer le type de CSS à sauvegarder selon l'onglet actif
let cssType = "page";
if (currentActiveTab === "page") {
cssType = "page";
window.aiBuilderPageCSS = newCSSContent;
} else if (currentActiveTab === "blocks") {
cssType = "block";
window.aiBuilderBlockCSS = newCSSContent;
}
// Sauvegarder dans les meta du post
await saveCSSInPostMeta(newCSSContent, cssType);
// Recharger le CSS combiné depuis le serveur après sauvegarde
await loadCSSFromPostMeta();
closeCSSModal();
// Afficher un message de confirmation
addMessage("CSS saved successfully!", "assistant");
};
// Fermer la modale en cliquant à l'extérieur
cssModal.onclick = (e) => {
if (e.target === cssModal) {
closeCSSModal();
}
};
function buildBlock(block) {
const { blockName, attrs = {}, innerBlocks = [] } = block;
return wp.blocks.createBlock(
blockName,
attrs,
innerBlocks.map(buildBlock) // récursivité ici
);
}
// Utilitaire pour récupérer le token JWT via AJAX WordPress
async function getJwtToken() {
if (!window.ajaxurl || !window.aiBuilderNonce) {
console.warn("AI Builder: Missing AJAX URL or nonce");
showAIMissingAccountToast();
throw new Error("Missing AJAX URL or nonce");
}
try {
const res = await fetch(window.ajaxurl, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `action=aibui_get_token&nonce=${window.aiBuilderNonce}`,
});
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
const data = await res.json();
console.log("token res: ", data);
if (data.success && data.data.token) {
return data.data.token;
}
showAIMissingAccountToast();
throw new Error(
"You need to have an account and be logged in to use AI features."
);
} catch (error) {
console.error("Error fetching JWT token:", error);
showAIMissingAccountToast();
throw error;
}
}
// Fonction utilitaire pour afficher un toast UX si l'utilisateur n'a pas de compte/connexion
function showAIMissingAccountToast() {
if (document.getElementById("ai-missing-account-toast")) return; // Pas de doublon
const toast = document.createElement("div");
toast.id = "ai-missing-account-toast";
toast.innerHTML = `
You need to have an account and be logged in to use AI features.
Go to Account
`;
document.body.appendChild(toast);
setTimeout(() => {
if (toast.parentNode) toast.parentNode.removeChild(toast);
}, 8000);
}
function showNotEnoughCreditsToast() {
if (document.getElementById("ai-missing-account-toast")) return; // Pas de doublon
const toast = document.createElement("div");
toast.id = "ai-missing-account-toast";
toast.innerHTML = `
`;
document.body.appendChild(toast);
setTimeout(() => {
if (toast.parentNode) toast.parentNode.removeChild(toast);
}, 8000);
}
// Fonction pour afficher le nombre de crédits sous le titre ET sous le bouton
function updateCreditsDisplay(credits) {
// Sous le titre du chat
let creditsElem = document.getElementById("ai-credits-display");
if (!creditsElem) {
const header = document.querySelector("#chat-header-left");
if (header) {
creditsElem = document.createElement("div");
creditsElem.id = "ai-credits-display";
creditsElem.style.fontSize = "12px";
creditsElem.style.color = "#cccccc";
creditsElem.style.marginTop = "2px";
creditsElem.style.textAlign = "center";
header.appendChild(creditsElem);
}
}
if (creditsElem) {
creditsElem.textContent =
credits !== null ? `${credits} credits left` : "- credits left";
}
// À l'intérieur du bouton toggle
const toggle = document.getElementById("chat-toggle");
if (toggle) {
let span = toggle.querySelector(".ai-credits-inside");
if (!span) {
span = document.createElement("span");
span.className = "ai-credits-inside";
span.style.display = "block";
span.style.fontSize = "10px";
span.style.color = "#cccccc";
span.style.marginTop = "0px";
span.style.textAlign = "center";
toggle.appendChild(span);
}
span.textContent = credits !== null ? `${credits} credits` : "- credits";
}
}
window.updateAICreditsDisplay = updateCreditsDisplay;
// Fonction pour charger les crédits utilisateur
async function loadUserCredits() {
try {
const jwtToken = await getJwtToken();
const res = await fetch(window.config.apiUrl + "/user/profile", {
method: "GET",
headers: {
Authorization: `Bearer ${jwtToken}`,
"Content-Type": "application/json",
},
});
if (!res.ok) throw new Error("Failed to load profile");
const data = await res.json();
const aiCredits = data.user?.aiCredits || {};
const totalCredits =
(aiCredits.onAccountCreation || 0) +
(aiCredits.monthlySubscription || 0) +
(aiCredits.paid || 0);
console.log("Credits loaded:", totalCredits, "from:", aiCredits);
updateCreditsDisplay(totalCredits);
} catch (e) {
console.error("Error loading credits:", e);
updateCreditsDisplay(null);
}
}
// Fonction pour injecter le CSS dans l'éditeur WordPress
function injectCSSInEditor(cssContent) {
// Créer un style tag pour l'éditeur
const styleId = "ai-builder-editor-css";
let styleElement = document.getElementById(styleId);
if (!styleElement) {
styleElement = document.createElement("style");
styleElement.id = styleId;
styleElement.type = "text/css";
document.head.appendChild(styleElement);
}
styleElement.textContent = cssContent;
}
// Fonction pour injecter le CSS dans le frontend
function injectCSSInFrontend(cssContent) {
// Créer un style tag pour le frontend
const styleId = "ai-builder-frontend-css";
let styleElement = document.getElementById(styleId);
if (!styleElement) {
styleElement = document.createElement("style");
styleElement.id = styleId;
styleElement.type = "text/css";
document.head.appendChild(styleElement);
}
styleElement.textContent = cssContent;
}
// Fonction pour sauvegarder le CSS dans les meta du post via AJAX WordPress
async function saveCSSInPostMeta(cssContent, cssType = "page") {
try {
const postId = wp.data.select("core/editor").getCurrentPostId();
const formData = new FormData();
formData.append("action", "aibui_save_post_css");
formData.append("nonce", window.aiBuilderNonce);
formData.append("post_id", postId);
formData.append("css_content", cssContent);
formData.append("css_type", cssType);
await fetch(window.ajaxurl, {
method: "POST",
body: formData,
});
} catch (err) {
console.error("Error saving CSS to post meta:", err);
}
}
// Set the meta description field by id once (no retry)
async function setMetaDescriptionField(value) {
const el = document.getElementById('aibui_meta_description_field');
if (!el) return false;
el.value = value || '';
const evt = new Event('input', { bubbles: true });
el.dispatchEvent(evt);
return true;
}
// Update post title locally in the editor
function updatePostTitle(title) {
if (!title) return false;
try {
// Update the title in WordPress editor state
wp.data.dispatch('core/editor').editPost({ title: title });
return true;
} catch (err) {
console.error('Error updating post title:', err);
return false;
}
}
// Mark page as created via AI
async function markPageAsAICreated() {
try {
const postId =
wp?.data?.select("core/editor")?.getCurrentPostId?.() ||
new URL(window.location.href, window.location.origin).searchParams.get("post") ||
new URL(window.location.href, window.location.origin).searchParams.get("postId") ||
new URL(window.location.href, window.location.origin).searchParams.get("p");
if (!postId) {
console.log("No post ID found to mark as AI-created");
return;
}
const formData = new FormData();
formData.append("action", "aibui_mark_ai_created");
formData.append("post_id", postId);
formData.append("nonce", aiBuilderVars.nonce);
const response = await fetch(ajaxurl, {
method: "POST",
body: formData,
});
const result = await response.json();
if (result.success) {
console.log("Page marked as AI-created");
// Injecter le CSS admin immédiatement après marquage
injectAICreatedAdminCSS();
} else {
console.error("Failed to mark page as AI-created:", result);
}
} catch (error) {
console.error("Error marking page as AI-created:", error);
}
}
// Injecter le CSS admin pour masquer le titre
function injectAICreatedAdminCSS() {
// Vérifier si le style existe déjà
if (document.getElementById("aibui-hide-ai-title-admin")) {
return;
}
const style = document.createElement("style");
style.id = "aibui-hide-ai-title-admin";
style.type = "text/css";
style.textContent = `
/* Masquer le titre dans l'éditeur Gutenberg */
.editor-post-title,
.editor-post-title__input,
.edit-post-visual-editor__post-title-wrapper,
.editor-post-title__block,
.wp-block[data-type="core/post-title"],
.block-editor-block-list__block[data-type="core/post-title"],
.wp-block-post-title.editor-post-title__block {
display: none !important;
visibility: hidden !important;
height: 0 !important;
margin: 0 !important;
padding: 0 !important;
overflow: hidden !important;
opacity: 0 !important;
}
.edit-post-visual-editor__post-title-wrapper {
display: none !important;
visibility: hidden !important;
height: 0 !important;
margin: 0 !important;
padding: 0 !important;
overflow: hidden !important;
}
`;
document.head.appendChild(style);
}
// Save meta description in post meta via WordPress AJAX
async function saveMetaDescriptionInPostMeta(metaDesc) {
try {
const postId = wp.data.select("core/editor").getCurrentPostId();
const formData = new FormData();
formData.append("action", "aibui_save_meta_description");
formData.append("nonce", window.aiBuilderNonce);
formData.append("post_id", postId);
formData.append("meta_desc", metaDesc || "");
await fetch(window.ajaxurl, { method: "POST", body: formData });
} catch (err) {
console.error("Error saving meta description:", err);
}
}
// Attendre que l'ID du post soit disponible (l'éditeur met un peu de temps à charger)
async function waitForCurrentPostId(maxAttempts = 50, intervalMs = 100) {
for (let attemptIndex = 0; attemptIndex < maxAttempts; attemptIndex++) {
try {
const postId = wp?.data?.select("core/editor")?.getCurrentPostId?.();
if (postId) return postId;
} catch (e) { }
await new Promise((resolve) => setTimeout(resolve, intervalMs));
}
return null;
}
// Fonction pour charger le CSS depuis les meta du post via AJAX WordPress
async function loadCSSFromPostMeta() {
try {
const postId = wp.data.select("core/editor").getCurrentPostId();
const formData = new FormData();
formData.append("action", "aibui_get_post_css");
formData.append("nonce", window.aiBuilderNonce);
formData.append("post_id", postId);
const res = await fetch(window.ajaxurl, {
method: "POST",
body: formData,
});
if (res.ok) {
const data = await res.json();
if (data.success && data.data) {
// Stocker les CSS séparément
window.aiBuilderPageCSS = data.data.pageCss || "";
window.aiBuilderBlockCSS = data.data.blockCss || "";
// Utiliser le CSS combiné pour l'affichage
const combinedCSS = data.data.combinedCss || "";
currentCSSContent = combinedCSS;
// Injecter le CSS dans l'éditeur et le frontend
injectCSSInEditor(combinedCSS);
injectCSSInFrontend(combinedCSS);
// Afficher le bouton CSS s'il y a du CSS
if (combinedCSS.trim()) {
cssEditButton.style.display = "block";
}
console.log(
"CSS loaded - Page:",
window.aiBuilderPageCSS.length,
"chars, Blocks:",
window.aiBuilderBlockCSS.length,
"chars"
);
}
}
} catch (err) {
console.error("Error loading CSS from post meta:", err);
}
}
// Initialiser le chargement du CSS une fois que l'ID du post est prêt
(async function initEditorCssLoad() {
// Vérifier que les variables AJAX sont disponibles
if (!window.ajaxurl || !window.aiBuilderNonce) {
console.warn("AI Builder: AJAX variables not available, skipping CSS load");
return;
}
// Attendre un peu pour que WordPress soit complètement chargé
await new Promise(resolve => setTimeout(resolve, 1000));
const postId = await waitForCurrentPostId();
if (postId) {
loadCSSFromPostMeta();
} else {
console.warn(
"AI Builder: unable to resolve current post ID to load CSS."
);
}
})();
function getPatternName() {
try {
const isPatternEditor = (typeof aiBuilderVars !== 'undefined' && !!aiBuilderVars.isPatternEditor) || window.location.pathname.includes('site-editor.php');
if (!isPatternEditor) return '';
const url = new URL(window.location.href);
const p = url.searchParams.get('p') || url.searchParams.get('postId');
if (!p) return '';
const decoded = decodeURIComponent(p);
// Use correct WordPress terminology: template part (header/footer)
return decoded;
} catch (e) {
return '';
}
}
async function sendMessageAIV3() {
const messages = document.getElementById("chat-messages");
const input = document.querySelector("#chat-input textarea");
// const undoBtn = document.getElementById("chat-undo");
const sendBtn = document.querySelector("#chat-input button");
const toggleBtn = document.getElementById("chat-toggle");
const question = input.value.trim();
// Vérifier qu'un prompt est présent
let finalQuestion = question;
const patternName = getPatternName();
if (!finalQuestion) {
addMessage("Please enter a prompt first.", "assistant");
return;
}
const conversationHistory = getConversationHistoryString(6);
// 🔒 Désactiver le bouton + animation loading
sendBtn.disabled = true;
sendBtn.classList.add("loading");
sendBtn.textContent = "Generating...";
// 🎨 Activer l'effet visuel sur le toggle
if (toggleBtn) {
toggleBtn.classList.add("generating");
}
// Ajouter le message utilisateur à l'historique
addMessage(finalQuestion, "user");
input.value = "";
messages.scrollTop = messages.scrollHeight;
// Sauvegarder les blocs actuels
previousBlocks = wp.data.select("core/block-editor").getBlocks();
try {
// Récupérer le token JWT
const jwtToken = await getJwtToken();
console.log('window.config: ', window.config);
let res
if (patternName) {
const payload = {
userPrompt: finalQuestion,
// pageContent: pageContent,
patternName: patternName,
conversationHistory,
};
res = await fetch(
window.config.apiUrl + "/ai-transform-page/generate-pattern",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwtToken}`,
},
body: JSON.stringify(payload),
}
);
} else {
const payload = {
userPrompt: finalQuestion,
// pageContent: pageContent,
conversationHistory,
};
res = await fetch(
window.config.apiUrl + "/ai-transform-page/v2-page-generation",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwtToken}`,
},
body: JSON.stringify(payload),
}
);
}
const data = await res.json();
console.log("data: ", data);
console.log(data.pageContent);
if (data.error === "not-enough-credits") {
showNotEnoughCreditsToast();
return;
}
if (data.pageContent) {
if (data.pageContent !== "[-no-content-to-return-]") {
// Convertir chaque bloc JSON en bloc WordPress
const newBlocks = data.pageContent.map(buildBlock);
// 🔄 Supprimer tous les blocs existants
wp.data.dispatch("core/block-editor").resetBlocks([]);
// ➕ Insérer les nouveaux blocs
wp.data.dispatch("core/block-editor").insertBlocks(newBlocks);
}
// Handle CSS if present
if (data.cssContent && data.cssContent !== "[-no-content-to-return-]") {
console.log("Injecting CSS...");
// Sauvegarder le CSS dans les meta du post (type 'page')
await saveCSSInPostMeta(data.cssContent, "page");
// Recharger le CSS combiné depuis le serveur
await loadCSSFromPostMeta();
// Afficher le bouton CSS
cssEditButton.style.display = "block";
}
// Handle meta description if present
if (data.postMetaDesc && data.postMetaDesc !== "[-no-content-to-return-]") {
// await saveMetaDescriptionInPostMeta(data.postMetaDesc);
await setMetaDescriptionField(data.postMetaDesc);
}
// Handle title if present
if (data.postTitle && data.postTitle !== "[-no-content-to-return-]") {
updatePostTitle(data.postTitle);
}
let aiResponse =
"Page content has been updated with AI-generated content.";
if (data.aiResponse) {
aiResponse = data.aiResponse;
}
addMessage(aiResponse, "assistant");
// Marquer la page comme créée via IA
await markPageAsAICreated();
// undoBtn.style.display = "block"; // Affiche le bouton "Annuler"
// Mettre à jour les crédits si la réponse contient creditsLeft
if (typeof data.creditsLeft !== "undefined") {
updateCreditsDisplay(data.creditsLeft);
}
} else {
addMessage("Empty or invalid response.", "assistant");
}
} catch (err) {
addMessage("Internal server error", "assistant");
console.log("err : ", err);
} finally {
// 🔓 Réactiver le bouton + retirer animation
sendBtn.disabled = false;
sendBtn.classList.remove("loading");
sendBtn.textContent = "Generate";
messages.scrollTop = messages.scrollHeight;
// 🎨 Désactiver l'effet visuel sur le toggle
if (toggleBtn) {
toggleBtn.classList.remove("generating");
}
}
}
sendBtn.onclick = sendMessageAIV3;
input.addEventListener("keypress", (e) => {
if (e.key === "Enter") sendMessageAIV3();
});
loadUserCredits();
loadCSSFromPostMeta();
});
// Ajouter les styles CSS pour le bouton CSS et la modale
const cssStyles = document.createElement("style");
cssStyles.textContent = `
/* Header du chat avec bouton CSS intégré */
#chat-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 10px;
}
#chat-header-left {
flex: 1;
}
#chat-header-left h3 {
margin: 0 0 5px 0;
}
#chat-header-left p {
margin: 0;
font-size: 12px;
}
/* Bouton CSS intégré dans le header */
#css-edit-button {
width: 32px;
height: 32px;
background: #007cba;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.3s ease;
padding: 0;
}
#css-edit-button:hover {
background: #005a87;
transform: scale(1.05);
}
#css-edit-button svg {
width: 16px;
height: 16px;
}
#css-edit-button img {
width: 16px;
height: 16px;
display: block;
margin: 0 auto;
}
/* Modale CSS */
#css-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
#css-modal-content {
background: white;
border-radius: 8px;
width: 80%;
max-width: 800px;
max-height: 80%;
display: flex;
flex-direction: column;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
#css-modal-header {
padding: 20px;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
}
#css-modal-header h3 {
margin: 0;
color: #333;
}
/* Onglets CSS */
#css-modal-tabs {
display: flex;
border-bottom: 1px solid #ddd;
background: #f8f9fa;
}
.css-tab {
flex: 1;
padding: 12px 20px;
border: none;
background: transparent;
cursor: pointer;
font-size: 14px;
font-weight: 500;
color: #666;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
}
.css-tab:hover {
background: #e9ecef;
color: #333;
}
.css-tab.active {
color: #007cba;
border-bottom-color: #007cba;
background: white;
}
#css-modal-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
#css-modal-close:hover {
color: #000;
}
#css-modal-body {
flex: 1;
padding: 20px;
overflow: hidden;
}
#css-editor {
width: 100%;
height: 400px;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
line-height: 1.5;
resize: vertical;
box-sizing: border-box;
}
#css-editor:focus {
outline: none;
border-color: #007cba;
box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2);
}
#css-modal-footer {
padding: 20px;
border-top: 1px solid #ddd;
display: flex;
gap: 10px;
justify-content: flex-end;
}
#css-save, #css-cancel {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
}
#css-save {
background: #007cba;
color: white;
}
#css-save:hover {
background: #005a87;
}
#css-cancel {
background: #f0f0f0;
color: #333;
}
#css-cancel:hover {
background: #e0e0e0;
}
`;
document.head.appendChild(cssStyles);