PluginProbe
ZIP AI – AI Website Builder & AI Agent / 0.0.12
ZIP AI – AI Website Builder & AI Agent v0.0.12
0.0.14 0.0.13 0.0.12 0.0.11 0.0.10 0.0.9 trunk 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8
← All changes | inc/react/react-manager.php +26 -126 0.0.8 → 0.0.12 View file →
@@ -155,11 +155,18 @@
155 155 $asset_version
156 156 );
157 157
158 158 // ── React app styles ──
159 + // Version with the same build hash as the JS bundle (not the static
160 + // plugin version) so a rebuild that doesn't bump ZIPAI_MCP_VERSION still
161 + // busts the CSS cache. Otherwise `?ver=<plugin version>` stays identical
162 + // across builds and browsers/CDNs serve a stale stylesheet against fresh
163 + // JS — newly added utility classes go missing until the version bumps.
159 164 $this->style_operations(
160 165 'chat-assistant',
161 - $this->build_url . 'css/dist/chat-assistant.css'
166 + $this->build_url . 'css/dist/chat-assistant.css',
167 + array(),
168 + $asset_version
162 169 );
163 170
164 171 // ── Gutenberg editor plugin (native post/page editor only) ──
165 172 // Uses the same narrowed gate as `isBlockEditor` so the editor RPC
@@ -195,33 +202,16 @@
195 202 'tool-hooks',
196 203 'zipwpIframeConfig',
197 204 array(
198 205 'nonce' => wp_create_nonce( 'zip_ai_iframe' ),
199 - 'restNonce' => wp_create_nonce( 'wp_rest' ),
200 206 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
201 207 'displayMode' => $this->is_fullpage_screen() ? 'fullpage' : 'sidebar',
202 208 'adminHomeUrl' => admin_url(),
203 209 'userId' => get_current_user_id(),
204 210 'authUrl' => $auth_url,
205 - 'websiteContext' => array(
206 - 'site_url' => get_site_url(),
207 - 'admin_url' => admin_url(),
208 - 'site_title' => get_bloginfo( 'name' ),
209 - 'site_tagline' => get_bloginfo( 'description' ),
210 - 'language' => get_bloginfo( 'language' ),
211 - 'timezone' => wp_timezone_string(),
212 - 'date_format' => get_option( 'date_format' ),
213 - 'time_format' => get_option( 'time_format' ),
214 - 'is_multisite' => is_multisite(),
215 - ),
216 - 'pageContext' => $this->get_current_page_context(),
217 211 'activeProduct' => Product_Context::detect(),
218 212 'isBlockEditor' => $this->is_block_editor_screen(),
219 213 'isPostEditScreen' => $this->is_post_edit_screen(),
220 - 'themeContext' => array(
221 - 'color_palette' => $this->get_theme_color_palette(),
222 - ),
223 - 'installedPlugins' => $this->get_installed_plugins_versions(),
224 214 'setupGate' => $this->get_setup_gate(),
225 215 )
226 216 );
227 217
@@ -248,12 +238,28 @@
248 238 if ( ! is_array( $brand_context ) ) {
249 239 $brand_context = array();
250 240 }
251 241
242 + // Free/guest temp-site expiry (ISO-8601 UTC). The zipwp-client mu-plugin
243 + // stores it in the `zipwp_site_data` option and we suppress ZipWP's own
244 + // admin-bar countdown when ZIP AI owns the surface, so the assistant
245 + // surfaces the timer itself. Absent on permanent/paid sites, and
246 + // suppressed once the site is reserved (no longer expiring).
247 + $zipwp_site_data = get_option( 'zipwp_site_data' );
248 + $expire_at = ( is_array( $zipwp_site_data ) && empty( $zipwp_site_data['reserve'] ) && ! empty( $zipwp_site_data['expire_at'] ) )
249 + ? $zipwp_site_data['expire_at']
250 + : null;
251 +
252 252 $this->localize_script(
253 253 'chat-assistant',
254 254 'ZIPAI_CONFIG',
255 255 array(
256 + // Turns on the bridge/apply-change console mirror (wp-bridge-host.js,
257 + // apply-change/handler.js). Those log lines were already written and
258 + // gated on this flag, but nothing ever set it — so the whole tracing
259 + // surface has been dead. Follows WP_DEBUG; override with the filter
260 + // for a UAT run on a site that isn't in debug mode.
261 + 'debug' => (bool) apply_filters( 'zipai_frontend_debug', defined( 'WP_DEBUG' ) && WP_DEBUG ),
256 262 'apiUrl' => rtrim( ZIPAI_MCP_CREDIT_SERVER_API, '/' ),
257 263 // Server base URL for direct calls (e.g. inline-edit). Set via the
258 264 // ZIPAI_BRAIN_URL constant (defined in loader.php); override in
259 265 // wp-config.php.
@@ -269,10 +275,10 @@
269 275 // the menu entry is hidden for anyone who would only get a
270 276 // permission error after clicking it.
271 277 'canManageConnection' => current_user_can( 'manage_options' ),
272 278 'displayMode' => $this->is_fullpage_screen() ? 'fullpage' : 'sidebar',
279 + 'adminHomeUrl' => admin_url(),
273 280 'fullPageUrl' => admin_url( 'options-general.php?page=zip-ai-assistant' ),
274 - 'adminHomeUrl' => admin_url(),
275 281 'userId' => get_current_user_id(),
276 282 'domain' => wp_parse_url( home_url(), PHP_URL_HOST ),
277 283 'site_url' => home_url(),
278 284 'user' => array(
@@ -279,9 +285,9 @@
279 285 'id' => get_current_user_id(),
280 286 'email' => Helper::get_setting( 'user_email', '' ),
281 287 'name' => Helper::get_setting( 'user_name', '' ),
282 288 ),
283 - 'isFreshSite' => (bool) get_option( 'fresh_site', false ),
289 + 'expireAt' => $expire_at,
284 290 // Brand context for on-brand palette generation (colour picker).
285 291 'brandContext' => $brand_context,
286 292 'nonce' => wp_create_nonce( 'zip_ai_iframe' ),
287 293 // wp_rest nonce — required by browser-side code that calls WP
@@ -300,9 +306,8 @@
300 306 // ZipWP chat loads. (Theme/plugin lifecycle MCP tools now run
301 307 // server-side and do not use this.)
302 308 'updatesNonce' => wp_create_nonce( 'updates' ),
303 309 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
304 - 'authUrl' => $auth_url,
305 310 )
306 311 );
307 312 }
308 313
@@ -452,48 +457,8 @@
452 457 $this->enqueue_script( 'bridge-host' );
453 458 }
454 459
455 460 /**
456 - * Get current page/post context from PHP.
457 - *
458 - * @since 1.0.0
459 - * @return array<string, int|string|null>
460 - */
461 - private function get_current_page_context() {
462 - global $post;
463 -
464 - $context = array(
465 - 'post_id' => null,
466 - 'post_type' => null,
467 - 'post_title' => null,
468 - 'post_status' => null,
469 - );
470 -
471 - if ( $post instanceof \WP_Post ) {
472 - $context['post_id'] = $post->ID;
473 - $context['post_type'] = $post->post_type;
474 - $context['post_title'] = $post->post_title;
475 - $context['post_status'] = $post->post_status;
476 - return $context;
477 - }
478 -
479 - if ( is_admin() ) {
480 - $post_id = isset( $_GET['post'] ) && is_scalar( $_GET['post'] ) ? absint( $_GET['post'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
481 - if ( $post_id ) {
482 - $admin_post = get_post( $post_id );
483 - if ( $admin_post instanceof \WP_Post ) {
484 - $context['post_id'] = $admin_post->ID;
485 - $context['post_type'] = $admin_post->post_type;
486 - $context['post_title'] = $admin_post->post_title;
487 - $context['post_status'] = $admin_post->post_status;
488 - }
489 - }
490 - }
491 -
492 - return $context;
493 - }
494 -
495 - /**
496 461 * Authoritative server-side check for whether the current screen is the
497 462 * NATIVE WordPress post/page editor (post.php / post-new.php, screen base
498 463 * 'post'). The JS bridge falls back to this when `wp.data` /
499 464 * `core/block-editor` are not yet initialized at iframe boot — without
@@ -575,73 +540,8 @@
575 540 $auth_middleware
576 541 );
577 542
578 543 return $auth_url;
579 - }
580 -
581 - /**
582 - * Get theme color palette formatted as CSS variables.
583 - *
584 - * @since 1.0.0
585 - * @return array<string, string>
586 - */
587 - private function get_theme_color_palette() {
588 - if ( ! function_exists( 'astra_get_palette_colors' ) ) {
589 - return array();
590 - }
591 -
592 - $palette_data = astra_get_palette_colors();
593 - if ( ! is_array( $palette_data ) ) {
594 - return array();
595 - }
596 -
597 - $current_palette = is_string( $palette_data['currentPalette'] ?? null ) ? $palette_data['currentPalette'] : '';
598 - $palettes = is_array( $palette_data['palettes'] ?? null ) ? $palette_data['palettes'] : array();
599 -
600 - if ( '' === $current_palette || empty( $palettes[ $current_palette ] ) ) {
601 - return array();
602 - }
603 -
604 - $colors = $palettes[ $current_palette ];
605 - if ( ! is_array( $colors ) ) {
606 - return array();
607 - }
608 -
609 - $formatted_palette = array();
610 -
611 - foreach ( $colors as $index => $color ) {
612 - if ( ! is_scalar( $color ) ) {
613 - continue;
614 - }
615 - $formatted_palette[ '--ast-global-color-' . $index ] = (string) $color;
616 - }
617 -
618 - return $formatted_palette;
619 - }
620 -
621 - /**
622 - * Get installed plugins with their versions.
623 - *
624 - * @since 1.0.0
625 - * @return array<string, string>
626 - */
627 - private function get_installed_plugins_versions() {
628 - if ( ! function_exists( 'get_plugins' ) ) {
629 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
630 - }
631 -
632 - $all_plugins = get_plugins();
633 - $plugin_versions = array();
634 -
635 - foreach ( $all_plugins as $plugin_file => $plugin_data ) {
636 - $slug = dirname( $plugin_file );
637 - if ( '.' === $slug ) {
638 - $slug = basename( $plugin_file, '.php' );
639 - }
640 - $plugin_versions[ $slug ] = is_string( $plugin_data['Version'] ?? null ) ? $plugin_data['Version'] : '0.0.0';
641 - }
642 -
643 - return $plugin_versions;
644 544 }
645 545
646 546 /**
647 547 * Build the Spectra setup-gate descriptor for the React notice.