| @@ -6,15 +6,15 @@ | ||
| 6 | 6 | |
| 7 | 7 | // Wait for Gutenberg editor readiness |
| 8 | 8 | await (async function waitForEditorReady() { |
| 9 | 9 | const start = Date.now(); |
| 10 | - const timeoutMs = 15000; | |
| 10 | + const timeoutMs = 40000; // Increased to 40 seconds for slower servers | |
| 11 | 11 | while (Date.now() - start < timeoutMs) { |
| 12 | 12 | if (window.wp && wp.blocks && wp.data && wp.data.select('core/block-editor')) { |
| 13 | 13 | const dispatcher = wp.data.dispatch('core/block-editor'); |
| 14 | 14 | if (dispatcher && typeof dispatcher.insertBlocks === 'function') break; |
| 15 | 15 | } |
| 16 | - await new Promise(r => setTimeout(r, 200)); | |
| 16 | + await new Promise(r => setTimeout(r, 300)); // Increased check interval from 200ms to 300ms | |
| 17 | 17 | } |
| 18 | 18 | })(); |
| 19 | 19 | if (!window.wp || !wp.blocks || !wp.data) return; |
| 20 | 20 | |
| @@ -33,17 +33,32 @@ | ||
| 33 | 33 | if (!closeBtn && dialog) { |
| 34 | 34 | const evt = new KeyboardEvent('keydown', { key: 'Escape', keyCode: 27, which: 27, bubbles: true }); |
| 35 | 35 | document.dispatchEvent(evt); |
| 36 | 36 | } |
| 37 | - }, 200); | |
| 37 | + }, 500); // Increased from 200ms to 500ms for slower servers | |
| 38 | 38 | } catch (e) { } |
| 39 | 39 | |
| 40 | - // Fetch generation by ID | |
| 40 | + // Fetch generation by ID with timeout | |
| 41 | + let timeoutId; | |
| 42 | + const controller = new AbortController(); | |
| 43 | + timeoutId = setTimeout(() => controller.abort(), 20000); // 20 second timeout | |
| 44 | + | |
| 41 | 45 | const res = await fetch(aiBuilderVars.ajaxurl, { |
| 42 | 46 | method: 'POST', |
| 43 | 47 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
| 44 | 48 | body: `action=aibui_get_generation&nonce=${aiBuilderVars.nonce}&id=${encodeURIComponent(genId)}`, |
| 49 | + signal: controller.signal, | |
| 45 | 50 | }); |
| 51 | + | |
| 52 | + clearTimeout(timeoutId); | |
| 53 | + | |
| 54 | + if (!res.ok) { | |
| 55 | + if (res.status === 500) { | |
| 56 | + console.error('Server error while loading generation'); | |
| 57 | + } | |
| 58 | + return; | |
| 59 | + } | |
| 60 | + | |
| 46 | 61 | const data = await res.json(); |
| 47 | 62 | if (!data.success) return; |
| 48 | 63 | |
| 49 | 64 | const gen = data.data; |
| @@ -108,51 +123,132 @@ | ||
| 108 | 123 | `; |
| 109 | 124 | document.head.appendChild(style); |
| 110 | 125 | } |
| 111 | 126 | |
| 112 | - // Save CSS/meta through existing chat-widget helpers if available | |
| 113 | - async function saveMetaAndCss() { | |
| 114 | - try { | |
| 115 | - if (gen.cssContent && typeof saveCSSInPostMeta === 'function' && typeof loadCSSFromPostMeta === 'function') { | |
| 116 | - await saveCSSInPostMeta(gen.cssContent, 'page'); | |
| 117 | - await loadCSSFromPostMeta(); | |
| 118 | - } | |
| 119 | - if (gen.metaDesc && typeof setMetaDescriptionField === 'function') { | |
| 120 | - await setMetaDescriptionField(gen.metaDesc); | |
| 121 | - } | |
| 122 | - } catch (e) { | |
| 123 | - // Silent fail | |
| 124 | - } | |
| 127 | + // Apply meta description field in the editor (DOM-based, no post ID needed) | |
| 128 | + if (gen.metaDesc && typeof setMetaDescriptionField === 'function') { | |
| 129 | + try { await setMetaDescriptionField(gen.metaDesc); } catch (e) { } | |
| 125 | 130 | } |
| 126 | - await saveMetaAndCss(); | |
| 127 | 131 | |
| 128 | - // Auto-save the post (leave as draft by default) | |
| 132 | + // Auto-save the post so the post ID is assigned before saving post meta | |
| 129 | 133 | try { |
| 130 | 134 | await wp.data.dispatch('core/editor').savePost(); |
| 131 | 135 | } catch (e) { } |
| 132 | 136 | |
| 137 | + // Save CSS/JS directly to post meta via AJAX (same meta keys as chat widget) | |
| 138 | + async function savePostMeta(postId) { | |
| 139 | + const ajaxUrl = aiBuilderVars.ajaxurl; | |
| 140 | + const nonce = aiBuilderVars.nonce; | |
| 141 | + | |
| 142 | + if (gen.cssContent) { | |
| 143 | + try { | |
| 144 | + const formData = new URLSearchParams(); | |
| 145 | + formData.append('action', 'aibui_save_post_css'); | |
| 146 | + formData.append('nonce', nonce); | |
| 147 | + formData.append('post_id', postId); | |
| 148 | + formData.append('css_content', gen.cssContent); | |
| 149 | + formData.append('css_type', 'page'); | |
| 150 | + await fetch(ajaxUrl, { | |
| 151 | + method: 'POST', | |
| 152 | + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
| 153 | + body: formData.toString(), | |
| 154 | + }); | |
| 155 | + } catch (e) { | |
| 156 | + console.error('AI Builder: failed to save CSS to post meta', e); | |
| 157 | + } | |
| 158 | + } | |
| 159 | + | |
| 160 | + if (gen.jsContent) { | |
| 161 | + try { | |
| 162 | + const formData = new URLSearchParams(); | |
| 163 | + formData.append('action', 'aibui_save_post_js'); | |
| 164 | + formData.append('nonce', nonce); | |
| 165 | + formData.append('post_id', postId); | |
| 166 | + formData.append('js_content', gen.jsContent); | |
| 167 | + formData.append('js_type', 'page'); | |
| 168 | + await fetch(ajaxUrl, { | |
| 169 | + method: 'POST', | |
| 170 | + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, | |
| 171 | + body: formData.toString(), | |
| 172 | + }); | |
| 173 | + } catch (e) { | |
| 174 | + console.error('AI Builder: failed to save JS to post meta', e); | |
| 175 | + } | |
| 176 | + } | |
| 177 | + } | |
| 178 | + | |
| 133 | 179 | // Mark generation as applied (attach pageId if available) |
| 134 | 180 | const postId = (wp.data.select('core/editor').getCurrentPostId && wp.data.select('core/editor').getCurrentPostId()) || 0; |
| 181 | + | |
| 182 | + // Save CSS/JS to post meta now that the post ID is known | |
| 183 | + if (postId) { | |
| 184 | + await savePostMeta(postId); | |
| 185 | + | |
| 186 | + // Inject CSS into editor DOM so it's visible immediately | |
| 187 | + if (gen.cssContent) { | |
| 188 | + const styleId = 'ai-builder-editor-css'; | |
| 189 | + let styleEl = document.getElementById(styleId); | |
| 190 | + if (!styleEl) { | |
| 191 | + styleEl = document.createElement('style'); | |
| 192 | + styleEl.id = styleId; | |
| 193 | + styleEl.type = 'text/css'; | |
| 194 | + document.head.appendChild(styleEl); | |
| 195 | + } | |
| 196 | + styleEl.textContent = gen.cssContent; | |
| 197 | + } | |
| 198 | + | |
| 199 | + // Inject JS into editor DOM so it runs immediately | |
| 200 | + if (gen.jsContent) { | |
| 201 | + const scriptId = 'ai-builder-editor-js'; | |
| 202 | + let scriptEl = document.getElementById(scriptId); | |
| 203 | + if (!scriptEl) { | |
| 204 | + scriptEl = document.createElement('script'); | |
| 205 | + scriptEl.id = scriptId; | |
| 206 | + scriptEl.type = 'text/javascript'; | |
| 207 | + document.head.appendChild(scriptEl); | |
| 208 | + } | |
| 209 | + scriptEl.textContent = gen.jsContent; | |
| 210 | + } | |
| 211 | + } | |
| 135 | 212 | try { |
| 213 | + const markController = new AbortController(); | |
| 214 | + const markTimeoutId = setTimeout(() => markController.abort(), 20000); // 20 second timeout | |
| 215 | + | |
| 136 | 216 | await fetch(aiBuilderVars.ajaxurl, { |
| 137 | 217 | method: 'POST', |
| 138 | 218 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
| 139 | 219 | body: `action=aibui_mark_generation_applied&nonce=${aiBuilderVars.nonce}&id=${encodeURIComponent(genId)}&page_id=${encodeURIComponent(postId)}`, |
| 220 | + signal: markController.signal, | |
| 140 | 221 | }); |
| 141 | - } catch (e) { } | |
| 142 | 222 | |
| 223 | + clearTimeout(markTimeoutId); | |
| 224 | + } catch (e) { | |
| 225 | + if (e.name !== 'AbortError') { | |
| 226 | + console.error('Error marking generation as applied:', e); | |
| 227 | + } | |
| 228 | + } | |
| 229 | + | |
| 143 | 230 | // Mark page as created via AI |
| 144 | 231 | if (postId) { |
| 145 | 232 | try { |
| 233 | + const markAiController = new AbortController(); | |
| 234 | + const markAiTimeoutId = setTimeout(() => markAiController.abort(), 20000); // 20 second timeout | |
| 235 | + | |
| 146 | 236 | await fetch(aiBuilderVars.ajaxurl, { |
| 147 | 237 | method: 'POST', |
| 148 | 238 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, |
| 149 | 239 | body: `action=aibui_mark_ai_created&nonce=${aiBuilderVars.nonce}&post_id=${encodeURIComponent(postId)}`, |
| 240 | + signal: markAiController.signal, | |
| 150 | 241 | }); |
| 242 | + | |
| 243 | + clearTimeout(markAiTimeoutId); | |
| 244 | + | |
| 151 | 245 | // Injecter le CSS admin immédiatement |
| 152 | 246 | injectAICreatedAdminCSS(); |
| 153 | 247 | } catch (e) { |
| 154 | - console.error('Error marking page as AI-created:', e); | |
| 248 | + if (e.name !== 'AbortError') { | |
| 249 | + console.error('Error marking page as AI-created:', e); | |
| 250 | + } | |
| 155 | 251 | } |
| 156 | 252 | } |
| 157 | 253 | |
| 158 | 254 | // Clean URL param to avoid reapplying on refresh |
| @@ -159,9 +255,14 @@ | ||
| 159 | 255 | const url = new URL(window.location.href); |
| 160 | 256 | url.searchParams.delete('aibui_gen_id'); |
| 161 | 257 | window.history.replaceState({}, '', url.toString()); |
| 162 | 258 | } catch (e) { |
| 163 | - // Silent | |
| 259 | + // Handle timeout/abort errors | |
| 260 | + if (e.name === 'AbortError' || e.message?.includes('timeout')) { | |
| 261 | + console.error('Timeout error while applying generation:', e); | |
| 262 | + } else { | |
| 263 | + console.error('Error applying generation:', e); | |
| 264 | + } | |
| 164 | 265 | } |
| 165 | 266 | }); |
| 166 | 267 | |
| 167 | 268 | |