Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/king-addons/includes/Admin.php
+++ b/king-addons/includes/Admin.php
@@ -216,7 +216,12 @@
// Check WooCommerce Builder extension toggle
$woo_builder_enabled = !isset($options['ext_woo-builder']) || $options['ext_woo-builder'] === 'enabled';
- if ($woo_builder_enabled && (defined('KING_ADDONS_EXT_WOO_BUILDER') ? KING_ADDONS_EXT_WOO_BUILDER : true)) {
+ if (
+ $woo_builder_enabled
+ && (defined('KING_ADDONS_EXT_WOO_BUILDER') ? KING_ADDONS_EXT_WOO_BUILDER : true)
+ && class_exists('WooCommerce')
+ && function_exists('WC')
+ ) {
$this->showWooBuilder();
}
--- a/king-addons/includes/Core.php
+++ b/king-addons/includes/Core.php
@@ -60,6 +60,11 @@
*/
private function isExtensionEnabled(string $extension_id, string $constant_name): bool
{
+ // Dependency checks (extensions that require other plugins).
+ if ($extension_id === 'woo-builder' && (!class_exists('WooCommerce') || !function_exists('WC'))) {
+ return false;
+ }
+
// Get options from database
$options = get_option('king_addons_options', []);
--- a/king-addons/includes/ModulesMap.php
+++ b/king-addons/includes/ModulesMap.php
@@ -17,8 +17,7 @@
'description' => esc_html__('Interactive 3D card with holographic film overlay, rim glow, and parallax effects. Features smooth pointer tracking with lerp animation for stunning visual depth.', 'king-addons'),
'php-class' => 'Holographic_Card',
'docs-link' => '',
- 'demo-link' => '',
- // 'demo-link' => 'https://kingaddons.com/elementor/holographic-card/',
+ 'demo-link' => 'https://kingaddons.com/elementor/3d-holographic-card/',
'css' => ['style'],
'js' => ['script'],
'category' => 'creative',
@@ -1740,6 +1739,117 @@
'has-pro' => false,
'category' => 'woocommerce',
],
+ // WooCommerce Shortcode Wrapper Widgets
+ 'woo-shortcode-cart' => [
+ 'title' => esc_html__('WC Cart (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders the standard WooCommerce cart page using the [woocommerce_cart] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Cart',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-checkout' => [
+ 'title' => esc_html__('WC Checkout (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders the standard WooCommerce checkout page using the [woocommerce_checkout] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Checkout',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-my-account' => [
+ 'title' => esc_html__('WC My Account (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders the standard WooCommerce My Account page using the [woocommerce_my_account] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_My_Account',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-products' => [
+ 'title' => esc_html__('WC Products (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders WooCommerce products using standard shortcodes with configurable query options.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Products',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-order-tracking' => [
+ 'title' => esc_html__('WC Order Tracking (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders the standard WooCommerce order tracking form using the [woocommerce_order_tracking] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Order_Tracking',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-product-page' => [
+ 'title' => esc_html__('WC Product Page (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders a full single product page by ID or SKU using the [product_page] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Product_Page',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-product-category' => [
+ 'title' => esc_html__('WC Product Category (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders products from a specific category using the [product_category] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Product_Category',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-product-categories' => [
+ 'title' => esc_html__('WC Product Categories (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders product categories grid using the [product_categories] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Product_Categories',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-add-to-cart' => [
+ 'title' => esc_html__('WC Add To Cart Button (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders an add-to-cart button for a specific product using the [add_to_cart] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Add_To_Cart',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
+ 'woo-shortcode-shop-messages' => [
+ 'title' => esc_html__('WC Shop Messages (Shortcode)', 'king-addons'),
+ 'description' => esc_html__('Renders WooCommerce shop messages and notices using the [shop_messages] shortcode.', 'king-addons'),
+ 'php-class' => 'Woo_Shortcode_Shop_Messages',
+ 'docs-link' => '',
+ 'demo-link' => '',
+ 'css' => [],
+ 'js' => [],
+ 'has-pro' => false,
+ 'category' => 'woocommerce',
+ ],
'tb-post-title' => [
'title' => esc_html__('TB - Post Title', 'king-addons'),
'description' => esc_html__('Displays current post title with tag and link options; Pro adds truncation and hover underline.', 'king-addons'),
@@ -2172,7 +2282,7 @@
'php-class' => 'Animated_Gradient_Mesh_Background',
'has-pro' => true,
'docs-link' => '',
- 'demo-link' => '',
+ 'demo-link' => 'https://kingaddons.com/elementor/interactive-gradient-mesh/',
'css' => ['style'],
'js' => ['script'],
],
--- a/king-addons/includes/admin/layouts/dashboard-v3/dashboard-v3.php
+++ b/king-addons/includes/admin/layouts/dashboard-v3/dashboard-v3.php
@@ -440,26 +440,46 @@
<div class="ka-v3-tab-content" id="ka-v3-tab-extensions">
<div class="ka-v3-grid">
<?php foreach ($extensions as $ext_id => $ext):
- $ext_enabled = !isset($options['ext_' . $ext_id]) || $options['ext_' . $ext_id] === 'enabled';
+ $ext_available = true;
+ $ext_requirement_msg = '';
+ $ext_requirement_url = '';
+
+ if ($ext_id === 'woo-builder' && (!class_exists('WooCommerce') || !function_exists('WC'))) {
+ $ext_available = false;
+ $ext_requirement_msg = $ext['requires']['woocommerce']['message'] ?? esc_html__('WooCommerce is required.', 'king-addons');
+ $ext_requirement_url = $ext['requires']['woocommerce']['install_url'] ?? admin_url('plugin-install.php?s=woocommerce&tab=search&type=term');
+ }
+
+ $ext_enabled = $ext_available && (!isset($options['ext_' . $ext_id]) || $options['ext_' . $ext_id] === 'enabled');
?>
- <div class="ka-v3-card <?php echo $ext_enabled ? '' : 'disabled'; ?>" data-ext="<?php echo esc_attr($ext_id); ?>" data-enabled="<?php echo $ext_enabled ? '1' : '0'; ?>">
+ <div class="ka-v3-card <?php echo $ext_enabled ? '' : 'disabled'; ?> <?php echo $ext_available ? '' : 'ka-v3-card-unavailable'; ?>" data-ext="<?php echo esc_attr($ext_id); ?>" data-enabled="<?php echo $ext_enabled ? '1' : '0'; ?>" data-available="<?php echo $ext_available ? '1' : '0'; ?>">
<div class="ka-v3-card-header">
<div class="ka-v3-card-icon">
<span class="dashicons <?php echo esc_attr($ext['icon']); ?>"></span>
</div>
<label class="ka-v3-toggle">
<input type="hidden" name="king_addons_options[ext_<?php echo esc_attr($ext_id); ?>]" value="disabled">
- <input type="checkbox" name="king_addons_options[ext_<?php echo esc_attr($ext_id); ?>]" value="enabled" <?php checked($ext_enabled); ?>>
+ <input type="checkbox" name="king_addons_options[ext_<?php echo esc_attr($ext_id); ?>]" value="enabled" <?php checked($ext_enabled); ?> <?php disabled(!$ext_available); ?>>
<span class="ka-v3-toggle-slider"></span>
</label>
</div>
<h4 class="ka-v3-card-title"><?php echo esc_html($ext['title']); ?></h4>
<p class="ka-v3-card-desc"><?php echo esc_html($ext['description']); ?></p>
+ <?php if (!$ext_available && $ext_requirement_msg !== ''): ?>
+ <p class="ka-v3-card-requirement"><?php echo esc_html($ext_requirement_msg); ?></p>
+ <?php endif; ?>
<div class="ka-v3-card-footer">
- <a href="<?php echo esc_url($ext['link']); ?>" class="ka-v3-card-link <?php echo !$ext_enabled ? 'ka-v3-link-disabled' : ''; ?>">
- <?php esc_html_e('Open Panel', 'king-addons'); ?>
- <span class="dashicons dashicons-arrow-right-alt"></span>
- </a>
+ <?php if ($ext_available): ?>
+ <a href="<?php echo esc_url($ext['link']); ?>" class="ka-v3-card-link <?php echo !$ext_enabled ? 'ka-v3-link-disabled' : ''; ?>">
+ <?php esc_html_e('Open Panel', 'king-addons'); ?>
+ <span class="dashicons dashicons-arrow-right-alt"></span>
+ </a>
+ <?php else: ?>
+ <a href="<?php echo esc_url($ext_requirement_url); ?>" class="ka-v3-card-link">
+ <?php esc_html_e('Install / Activate WooCommerce', 'king-addons'); ?>
+ <span class="dashicons dashicons-external"></span>
+ </a>
+ <?php endif; ?>
</div>
</div>
<?php endforeach; ?>
--- a/king-addons/includes/admin/layouts/dashboard-v3/extensions-list.php
+++ b/king-addons/includes/admin/layouts/dashboard-v3/extensions-list.php
@@ -43,6 +43,13 @@
'icon' => 'dashicons-cart',
'constant' => 'KING_ADDONS_EXT_WOO_BUILDER',
'link' => admin_url('admin.php?page=king-addons-woo-builder'),
+ 'requires' => [
+ 'woocommerce' => [
+ 'name' => esc_html__('WooCommerce', 'king-addons'),
+ 'install_url' => admin_url('plugin-install.php?s=woocommerce&tab=search&type=term'),
+ 'message' => esc_html__('Requires WooCommerce to be installed and activated.', 'king-addons'),
+ ],
+ ],
],
'cookie-consent' => [
'title' => esc_html__('Cookie / Consent Bar', 'king-addons'),
--- a/king-addons/includes/admin/layouts/woo-builder-page.php
+++ b/king-addons/includes/admin/layouts/woo-builder-page.php
@@ -557,16 +557,56 @@
margin-bottom: 56px;
}
-.ka-wb-header-content h1 {
+.ka-wb-header-content {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+}
+
+.ka-wb-header-titles {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ min-width: 0;
+}
+
+.ka-wb-header-titles h1 {
font-size: 56px;
font-weight: 700;
letter-spacing: -0.025em;
- margin: 0 0 8px;
+ margin: 0;
+ line-height: 1;
+}
+
+.ka-wb-header-titles p {
+ font-size: 21px;
+ color: var(--ka-wb-text-secondary);
+ margin: 0;
+ font-weight: 400;
+}
+
+.ka-wb-title-icon {
+ width: 76px;
+ height: 76px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 22px;
+ background: rgba(0, 113, 227, 0.12);
+ color: var(--ka-wb-accent);
+ flex: 0 0 auto;
+}
+body.ka-v3-dark .ka-wb-title-icon {
+ background: rgba(10, 132, 255, 0.18);
+ color: #0a84ff;
+}
+.ka-wb-title-icon svg { width: 36px; height: 36px; }
+
+.ka-wb-title-text {
background: linear-gradient(135deg, var(--ka-wb-text) 0%, var(--ka-wb-text-secondary) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
- line-height: 1;
}
.ka-wb-header-content p {
@@ -742,10 +782,28 @@
font-weight: 500;
color: var(--ka-wb-text);
text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+ transition: var(--ka-wb-transition);
+}
+
+.ka-wb-template-title-icon {
+ width: 18px;
+ height: 18px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--ka-wb-text-secondary);
+ flex: 0 0 auto;
+}
+.ka-wb-template-title-icon svg { width: 18px; height: 18px; }
+.ka-wb-template-title-text {
+ min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
- transition: var(--ka-wb-transition);
}
.ka-wb-template-title:hover {
@@ -1446,9 +1504,20 @@
gap: 24px;
}
- .ka-wb-header-content h1 {
+ .ka-wb-header-titles h1 {
font-size: 36px;
}
+
+ .ka-wb-title-icon {
+ width: 64px;
+ height: 64px;
+ border-radius: 18px;
+ }
+
+ .ka-wb-title-icon svg {
+ width: 30px;
+ height: 30px;
+ }
.ka-wb-types {
grid-template-columns: 1fr 1fr;
@@ -1507,8 +1576,18 @@
<!-- Header -->
<header class="ka-wb-header">
<div class="ka-wb-header-content">
- <h1><?php esc_html_e('WooCommerce Builder', 'king-addons'); ?></h1>
- <p><?php esc_html_e('Design beautiful store pages with Elementor', 'king-addons'); ?></p>
+ <span class="ka-wb-title-icon" aria-hidden="true">
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+ <path d="M6 6h15l-1.5 9h-12z" />
+ <path d="M6 6l-2-3H1" />
+ <circle cx="9" cy="20" r="1" />
+ <circle cx="18" cy="20" r="1" />
+ </svg>
+ </span>
+ <div class="ka-wb-header-titles">
+ <h1><span class="ka-wb-title-text"><?php esc_html_e('WooCommerce Builder', 'king-addons'); ?></span></h1>
+ <p><?php esc_html_e('Design beautiful store pages with Elementor', 'king-addons'); ?></p>
+ </div>
</div>
<div class="ka-wb-header-actions">
<?php ka_render_dark_theme_toggle(); ?>
@@ -1581,6 +1660,7 @@
<?php foreach ($templates as $template): ?>
<?php
$elementor_url = ka_woo_get_elementor_edit_url((int) $template['id']);
+ $title_icon = $types[$template['type']]['icon'] ?? '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>';
$duplicate_url = wp_nonce_url(add_query_arg([
'page' => 'king-addons-woo-builder',
'template_id' => $template['id'],
@@ -1603,7 +1683,10 @@
<div class="ka-wb-template-info">
<a href="<?php echo esc_url($elementor_url); ?>" class="ka-wb-template-title">
- <?php echo esc_html($template['title']); ?>
+ <span class="ka-wb-template-title-icon" aria-hidden="true">
+ <?php echo $title_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </span>
+ <span class="ka-wb-template-title-text"><?php echo esc_html($template['title']); ?></span>
</a>
<div class="ka-wb-template-meta">
<span class="ka-wb-template-type">
@@ -1755,7 +1838,7 @@
<span class="ka-wb-quickstart-step-number" aria-hidden="true">2</span>
<div>
<p class="ka-wb-quickstart-step-title"><?php esc_html_e('Design in Elementor', 'king-addons'); ?></p>
- <p class="ka-wb-quickstart-step-desc"><?php esc_html_e('You’ll be taken straight into the Elementor editor. Build your layout visually using widgets from "King Addons Woo Builder" category, then save.', 'king-addons'); ?></p>
+ <p class="ka-wb-quickstart-step-desc"><?php esc_html_e('You’ll be taken straight into the Elementor editor. Build your layout visually using widgets from "King Addons Woo Builder" category or WooCommerce related widgets provided by other compatible plugins, then save.', 'king-addons'); ?></p>
</div>
</div>
<div class="ka-wb-quickstart-step">
--- a/king-addons/includes/admin/shared/dark-theme.php
+++ b/king-addons/includes/admin/shared/dark-theme.php
@@ -45,6 +45,7 @@
<?php esc_html_e('Dark', 'king-addons'); ?>
</button>
<button type="button" class="ka-v3-segmented-btn" data-theme="auto" aria-pressed="<?php echo $ka_theme_mode === 'auto' ? 'true' : 'false'; ?>">
+ <span class="ka-v3-segmented-icon" aria-hidden="true">◐</span>
<?php esc_html_e('Auto', 'king-addons'); ?>
</button>
</div>
--- a/king-addons/includes/extensions/Custom_Cursor/Custom_Cursor.php
+++ b/king-addons/includes/extensions/Custom_Cursor/Custom_Cursor.php
@@ -26,6 +26,13 @@
public const OPTION_KEY = 'king_addons_custom_cursor_settings';
/**
+ * Admin screen hook suffix for the settings page.
+ *
+ * @var string
+ */
+ private string $settings_page_hook = '';
+
+ /**
* Constructor.
*/
public function __construct()
@@ -33,6 +40,7 @@
add_action('admin_init', [$this, 'register_settings']);
// Use priority 15 to ensure the parent menu exists before adding this submenu
add_action('admin_menu', [$this, 'register_settings_page'], 15);
+ add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
add_action('wp_enqueue_scripts', [$this, 'maybe_enqueue_assets']);
add_action('elementor/preview/enqueue_scripts', [$this, 'enqueue_preview_assets']);
add_action('wp_footer', [$this, 'render_cursor_markup']);
@@ -73,7 +81,7 @@
*/
public function register_settings_page(): void
{
- add_submenu_page(
+ $this->settings_page_hook = (string) add_submenu_page(
'king-addons',
esc_html__('Custom Cursor', 'king-addons'),
esc_html__('Custom Cursor', 'king-addons'),
@@ -84,6 +92,27 @@
}
/**
+ * Enqueue admin assets for the Custom Cursor settings page.
+ *
+ * @param string $hook_suffix Current admin page hook suffix.
+ *
+ * @return void
+ */
+ public function enqueue_admin_assets(string $hook_suffix): void
+ {
+ if ('' === $this->settings_page_hook) {
+ return;
+ }
+
+ if ($hook_suffix !== $this->settings_page_hook) {
+ return;
+ }
+
+ // Required for wp.media modal.
+ wp_enqueue_media();
+ }
+
+ /**
* Render admin settings page.
*
* @return void
@@ -228,6 +257,51 @@
height: 18px;
}
+ /* Select dropdown with arrow */
+ .ka-cc-v3 select {
+ width: 100%;
+ padding: 14px 48px 14px 18px !important;
+ font-size: 15px;
+ font-family: inherit;
+ font-weight: 400;
+ border: 1.5px solid rgba(0, 0, 0, 0.1);
+ border-radius: 12px;
+ background-color: #fff !important;
+ color: #1d1d1f;
+ transition: all 0.2s ease;
+ cursor: pointer;
+ -webkit-appearance: none !important;
+ -moz-appearance: none !important;
+ appearance: none !important;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%231d1d1f' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") !important;
+ background-repeat: no-repeat !important;
+ background-position: right 16px center !important;
+ background-size: 18px 18px !important;
+ }
+ body.ka-v3-dark .ka-cc-v3 select {
+ background-color: #2c2c2e !important;
+ border-color: rgba(255, 255, 255, 0.15);
+ color: #f5f5f7;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f7' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") !important;
+ }
+ .ka-cc-v3 select:hover {
+ border-color: rgba(168, 85, 247, 0.5);
+ }
+ .ka-cc-v3 select:focus {
+ outline: none;
+ border-color: #a855f7;
+ box-shadow: 0 0 0 3px rgba(168, 85, 247, 0.15);
+ }
+ .ka-cc-v3 select option {
+ background: #fff;
+ color: #1d1d1f;
+ padding: 10px;
+ }
+ body.ka-v3-dark .ka-cc-v3 select option {
+ background: #2c2c2e;
+ color: #f5f5f7;
+ }
+
/* Opacity range slider */
.ka-cc-v3 input[type="range"] {
-webkit-appearance: none;
@@ -284,15 +358,16 @@
}
.ka-cc-v3 .ka-preview-area {
height: 280px;
- display: flex;
- align-items: center;
- justify-content: center;
background: linear-gradient(135deg, #f5f5f7 0%, #e8e8ed 100%);
position: relative;
overflow: hidden;
+ cursor: default;
}
body.ka-v3-dark .ka-cc-v3 .ka-preview-area {
- background: linear-gradient(135deg, #2c2c2e 0%, #1c1c1e 100%);
+ background: linear-gradient(135deg, #48484a 0%, #3a3a3c 100%);
+ }
+ .ka-cc-v3 .ka-preview-area.ka-cc-hide-original {
+ cursor: none;
}
.ka-cc-v3 .ka-preview-area::before {
content: '';
@@ -306,9 +381,12 @@
background-image: radial-gradient(rgba(255,255,255,0.1) 1px, transparent 1px);
}
.ka-cc-v3 .ka-preview-cursor {
- position: relative;
+ position: absolute;
+ top: 0;
+ left: 0;
z-index: 1;
transition: all 0.15s ease;
+ pointer-events: none;
}
.ka-cc-v3 .ka-preview-cursor-inner {
border-radius: 50%;
@@ -380,9 +458,15 @@
color: #f5f5f7;
}
+ /* Ripple animation */
+ @keyframes ka-cc-ripple {
+ 0% { transform: scale(0); opacity: 0.5; }
+ 100% { transform: scale(2.5); opacity: 0; }
+ }
+
/* Responsive */
@media (max-width: 1200px) {
- .ka-cc-v3 { flex-direction: column; }
+ .ka-cc-v3 { flex-direction: column-reverse; }
.ka-cc-v3 .ka-cc-sidebar {
width: 100%;
order: -1;
@@ -411,9 +495,9 @@
</span>
<div class="ka-v3-segmented" id="ka-v3-theme-segment" role="radiogroup" aria-label="Theme" data-active="<?php echo esc_attr($theme_mode); ?>">
<span class="ka-v3-segmented-indicator" aria-hidden="true"></span>
- <button type="button" class="ka-v3-segmented-btn" data-theme="light" aria-pressed="<?php echo $theme_mode === 'light' ? 'true' : 'false'; ?>"><?php esc_html_e('Light', 'king-addons'); ?></button>
- <button type="button" class="ka-v3-segmented-btn" data-theme="dark" aria-pressed="<?php echo $theme_mode === 'dark' ? 'true' : 'false'; ?>"><?php esc_html_e('Dark', 'king-addons'); ?></button>
- <button type="button" class="ka-v3-segmented-btn" data-theme="auto" aria-pressed="<?php echo $theme_mode === 'auto' ? 'true' : 'false'; ?>"><?php esc_html_e('Auto', 'king-addons'); ?></button>
+ <button type="button" class="ka-v3-segmented-btn" data-theme="light" aria-pressed="<?php echo $theme_mode === 'light' ? 'true' : 'false'; ?>"><span class="ka-v3-segmented-icon" aria-hidden="true">☀︎</span><?php esc_html_e('Light', 'king-addons'); ?></button>
+ <button type="button" class="ka-v3-segmented-btn" data-theme="dark" aria-pressed="<?php echo $theme_mode === 'dark' ? 'true' : 'false'; ?>"><span class="ka-v3-segmented-icon" aria-hidden="true">☾</span><?php esc_html_e('Dark', 'king-addons'); ?></button>
+ <button type="button" class="ka-v3-segmented-btn" data-theme="auto" aria-pressed="<?php echo $theme_mode === 'auto' ? 'true' : 'false'; ?>"><span class="ka-v3-segmented-icon" aria-hidden="true">◐</span><?php esc_html_e('Auto', 'king-addons'); ?></button>
</div>
</div>
</div>
@@ -449,6 +533,16 @@
</div>
</div>
<div class="ka-row">
+ <div class="ka-row-label"><?php esc_html_e('Hide Original Cursor', 'king-addons'); ?></div>
+ <div class="ka-row-field">
+ <label class="ka-toggle">
+ <input type="checkbox" name="<?php echo esc_attr(self::OPTION_KEY); ?>[hide_original_cursor]" value="1" <?php checked(!empty($settings['hide_original_cursor'])); ?> />
+ <span class="ka-toggle-slider"></span>
+ <span class="ka-toggle-label"><?php esc_html_e('Hide default cursor (custom cursor follows it otherwise)', 'king-addons'); ?></span>
+ </label>
+ </div>
+ </div>
+ <div class="ka-row">
<div class="ka-row-label"><?php echo esc_html__('Rendering Mode', 'king-addons'); ?></div>
<div class="ka-row-field">
<select name="<?php echo esc_attr(self::OPTION_KEY); ?>[rendering_mode]" id="ka-cc-mode">
@@ -542,6 +636,63 @@
</div>
</div>
+ <!-- Image Cursor Settings (shown when type=image) -->
+ <div class="ka-card" id="ka-cc-image-card" style="display:none">
+ <div class="ka-card-header">
+ <span class="dashicons dashicons-format-image"></span>
+ <h2><?php echo esc_html__('Image Cursor', 'king-addons'); ?></h2>
+ </div>
+ <div class="ka-card-body">
+ <div class="ka-row">
+ <div class="ka-row-label"><?php echo esc_html__('Cursor Image', 'king-addons'); ?><?php if (!$is_pro): ?><span class="ka-pro-badge">PRO</span><?php endif; ?></div>
+ <div class="ka-row-field">
+ <div class="ka-image-upload-wrap" style="display:flex;align-items:center;gap:12px">
+ <div class="ka-image-preview" id="ka-cc-image-preview" style="width:64px;height:64px;border:2px dashed #d1d5db;border-radius:8px;display:flex;align-items:center;justify-content:center;background:#f9fafb;overflow:hidden">
+ <?php if (!empty($settings['image']['url'])): ?>
+ <img src="<?php echo esc_url($settings['image']['url']); ?>" style="max-width:100%;max-height:100%;object-fit:contain" />
+ <?php else: ?>
+ <span class="dashicons dashicons-plus-alt2" style="color:#9ca3af;font-size:24px"></span>
+ <?php endif; ?>
+ </div>
+ <div style="display:flex;flex-direction:column;gap:6px">
+ <button type="button" class="button" id="ka-cc-image-upload-btn" <?php disabled(!$is_pro); ?>><?php echo esc_html__('Select Image', 'king-addons'); ?></button>
+ <button type="button" class="button" id="ka-cc-image-remove-btn" style="color:#ef4444" <?php disabled(!$is_pro || empty($settings['image']['url'])); ?>><?php echo esc_html__('Remove', 'king-addons'); ?></button>
+ </div>
+ <input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][url]" id="ka-cc-image-url" value="<?php echo esc_attr($settings['image']['url']); ?>" />
+ </div>
+ </div>
+ </div>
+ <div class="ka-row">
+ <div class="ka-row-label"><?php echo esc_html__('Image Size', 'king-addons'); ?></div>
+ <div class="ka-row-field">
+ <input type="number" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][size]" value="<?php echo esc_attr((int) $settings['image']['size']); ?>" min="8" max="256" id="ka-cc-image-size" <?php disabled(!$is_pro); ?> /> <span style="color:#64748b">px</span>
+ </div>
+ </div>
+ <div class="ka-row">
+ <div class="ka-row-label"><?php echo esc_html__('Hotspot X', 'king-addons'); ?></div>
+ <div class="ka-row-field">
+ <input type="number" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][hotspot_x]" value="<?php echo esc_attr((int) $settings['image']['hotspot_x']); ?>" min="0" max="256" id="ka-cc-image-hotspot-x" <?php disabled(!$is_pro); ?> /> <span style="color:#64748b">px</span>
+ </div>
+ </div>
+ <div class="ka-row">
+ <div class="ka-row-label"><?php echo esc_html__('Hotspot Y', 'king-addons'); ?></div>
+ <div class="ka-row-field">
+ <input type="number" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][hotspot_y]" value="<?php echo esc_attr((int) $settings['image']['hotspot_y']); ?>" min="0" max="256" id="ka-cc-image-hotspot-y" <?php disabled(!$is_pro); ?> /> <span style="color:#64748b">px</span>
+ </div>
+ </div>
+ <div class="ka-row">
+ <div class="ka-row-label"><?php echo esc_html__('Retina (2x)', 'king-addons'); ?></div>
+ <div class="ka-row-field">
+ <label class="ka-toggle">
+ <input type="checkbox" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][retina]" value="1" <?php checked(!empty($settings['image']['retina'])); ?> <?php disabled(!$is_pro); ?> />
+ <span class="ka-toggle-slider"></span>
+ <span class="ka-toggle-label"><?php echo esc_html__('Image is @2x for Retina displays', 'king-addons'); ?></span>
+ </label>
+ </div>
+ </div>
+ </div>
+ </div>
+
<!-- States -->
<div class="ka-card">
<div class="ka-card-header">
@@ -648,11 +799,6 @@
<input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[targeting][include_urls]" value="<?php echo esc_attr(implode(',', $settings['targeting']['include_urls'])); ?>" />
<input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[targeting][exclude_pages]" value="<?php echo esc_attr(implode(',', $settings['targeting']['exclude_pages'])); ?>" />
<input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[targeting][exclude_post_types]" value="<?php echo esc_attr(implode(',', $settings['targeting']['exclude_post_types'])); ?>" />
- <input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][url]" value="<?php echo esc_attr($settings['image']['url']); ?>" />
- <input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][size]" value="<?php echo esc_attr($settings['image']['size']); ?>" />
- <input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][hotspot_x]" value="<?php echo esc_attr($settings['image']['hotspot_x']); ?>" />
- <input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][hotspot_y]" value="<?php echo esc_attr($settings['image']['hotspot_y']); ?>" />
- <input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[image][retina]" value="<?php echo !empty($settings['image']['retina']) ? '1' : ''; ?>" />
<input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[magnetic][enabled]" value="<?php echo !empty($settings['magnetic']['enabled']) ? '1' : ''; ?>" />
<input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[magnetic][strength]" value="<?php echo esc_attr($settings['magnetic']['strength']); ?>" />
<input type="hidden" name="<?php echo esc_attr(self::OPTION_KEY); ?>[magnetic][radius]" value="<?php echo esc_attr($settings['magnetic']['radius']); ?>" />
@@ -853,6 +999,9 @@
'gradient': 'Gradient',
'image': 'Image'
};
+
+ // Used to keep the cursor centered when preview is not hovered
+ let previewVisualSize = 14;
function updatePreview() {
const size = parseInt(sizeInput.value) || 14;
@@ -887,6 +1036,7 @@
// Base outer ring
const outerSize = size + (borderWidth * 2) + 8;
+ previewVisualSize = Math.max(size, outerSize);
outer.style.width = outerSize + 'px';
outer.style.height = outerSize + 'px';
outer.style.border = borderWidth + 'px solid ' + border;
@@ -977,6 +1127,7 @@
inner.style.borderRadius = '60% 40% 30% 70% / 60% 30% 70% 40%';
inner.style.width = (size * 1.3) + 'px';
inner.style.height = (size * 1.3) + 'px';
+ previewVisualSize = Math.max(previewVisualSize, (size * 1.3));
break;
case 'dual':
@@ -996,6 +1147,7 @@
outer2.style.top = '50%';
outer2.style.left = '50%';
outer2.style.opacity = '0.3';
+ previewVisualSize = Math.max(previewVisualSize, outer2Size);
break;
case 'gradient':
@@ -1003,13 +1155,28 @@
inner.style.background = 'linear-gradient(135deg, ' + fill + ' 0%, ' + border + ' 100%)';
inner.style.width = (size * 1.2) + 'px';
inner.style.height = (size * 1.2) + 'px';
+ previewVisualSize = Math.max(previewVisualSize, (size * 1.2));
break;
case 'image':
inner.style.display = 'block';
- inner.style.background = '#ddd';
- inner.style.border = '2px dashed #999';
inner.style.borderRadius = '4px';
+ inner.style.background = 'transparent';
+ const imgUrl = document.getElementById('ka-cc-image-url');
+ const imgSize = document.getElementById('ka-cc-image-size');
+ const imgSizeVal = parseInt(imgSize?.value) || size;
+ inner.style.width = imgSizeVal + 'px';
+ inner.style.height = imgSizeVal + 'px';
+ if (imgUrl && imgUrl.value) {
+ inner.style.backgroundImage = 'url(' + imgUrl.value + ')';
+ inner.style.backgroundSize = 'contain';
+ inner.style.backgroundRepeat = 'no-repeat';
+ inner.style.backgroundPosition = 'center';
+ } else {
+ inner.style.background = '#ddd';
+ inner.style.border = '2px dashed #999';
+ }
+ previewVisualSize = Math.max(previewVisualSize, imgSizeVal);
break;
default:
@@ -1027,6 +1194,17 @@
infoFill.style.background = fill;
infoBorder.style.background = border;
}
+
+ function centerPreviewCursor() {
+ const width = previewArea ? previewArea.clientWidth : 0;
+ const height = previewArea ? previewArea.clientHeight : 0;
+ const half = (previewVisualSize || 14) / 2;
+ cursorX = (width / 2) - half;
+ cursorY = (height / 2) - half;
+ targetX = cursorX;
+ targetY = cursorY;
+ cursor.style.transform = 'translate(' + cursorX + 'px, ' + cursorY + 'px)';
+ }
// Color picker sync
fillPicker.addEventListener('input', function() {
@@ -1091,26 +1269,78 @@
let isHovering = false;
let cursorX = 0, cursorY = 0;
let targetX = 0, targetY = 0;
+ let firstMove = true;
+ const hideOriginalInput = document.querySelector('input[name="<?php echo esc_attr(self::OPTION_KEY); ?>[hide_original_cursor]"]');
+
+ function updatePreviewCursor() {
+ if (!hideOriginalInput) {
+ return;
+ }
+ previewArea.classList.toggle('ka-cc-hide-original', !!hideOriginalInput.checked);
+ }
+
+ if (hideOriginalInput) {
+ hideOriginalInput.addEventListener('change', updatePreviewCursor);
+ updatePreviewCursor();
+ }
- previewArea.addEventListener('mouseenter', function() {
+ previewArea.addEventListener('mouseenter', function(e) {
isHovering = true;
+ firstMove = true;
+ // Immediately position cursor at mouse entry point
+ const rect = previewArea.getBoundingClientRect();
+ const size = parseInt(sizeInput.value) || 14;
+ targetX = e.clientX - rect.left - (size / 2);
+ targetY = e.clientY - rect.top - (size / 2);
+ // Set cursor position instantly on enter
+ cursorX = targetX;
+ cursorY = targetY;
+ cursor.style.transform = 'translate(' + cursorX + 'px, ' + cursorY + 'px)';
});
previewArea.addEventListener('mouseleave', function() {
isHovering = false;
- cursor.style.transform = '';
+ centerPreviewCursor();
+ firstMove = true;
});
previewArea.addEventListener('mousemove', function(e) {
if (!isHovering) return;
const rect = previewArea.getBoundingClientRect();
- targetX = e.clientX - rect.left - 20;
- targetY = e.clientY - rect.top - 20;
+ const size = parseInt(sizeInput.value) || 14;
+ targetX = e.clientX - rect.left - (size / 2);
+ targetY = e.clientY - rect.top - (size / 2);
+ // On first move after enter, snap to position
+ if (firstMove) {
+ cursorX = targetX;
+ cursorY = targetY;
+ firstMove = false;
+ }
+ });
+
+ // Click ripple effect in preview
+ previewArea.addEventListener('mousedown', function(e) {
+ if (!isHovering) return;
+ // Create ripple element
+ const ripple = document.createElement('div');
+ const rect = previewArea.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+ ripple.style.cssText = 'position:absolute;border-radius:50%;background:' + (fillInput.value || '#a855f7') + ';opacity:0.4;pointer-events:none;transform:scale(0);animation:ka-cc-ripple 0.6s ease-out forwards;';
+ ripple.style.left = (x - 30) + 'px';
+ ripple.style.top = (y - 30) + 'px';
+ ripple.style.width = '60px';
+ ripple.style.height = '60px';
+ previewArea.appendChild(ripple);
+ setTimeout(function() { ripple.remove(); }, 600);
+ // Scale down cursor briefly
+ cursor.style.transition = 'transform 0.1s ease';
+ setTimeout(function() { cursor.style.transition = 'none'; }, 100);
});
// Smooth animation
function animateCursor() {
if (isHovering) {
- cursorX += (targetX - cursorX) * 0.15;
- cursorY += (targetY - cursorY) * 0.15;
+ cursorX += (targetX - cursorX) * 0.18;
+ cursorY += (targetY - cursorY) * 0.18;
cursor.style.transform = 'translate(' + cursorX + 'px, ' + cursorY + 'px)';
}
requestAnimationFrame(animateCursor);
@@ -1119,6 +1349,61 @@
// Initial update
updatePreview();
+ centerPreviewCursor();
+
+ // ====== Image Cursor Card visibility ======
+ const imageCard = document.getElementById('ka-cc-image-card');
+ function updateImageCardVisibility() {
+ if (imageCard && typeInput) {
+ imageCard.style.display = typeInput.value === 'image' ? '' : 'none';
+ }
+ }
+ if (typeInput) {
+ typeInput.addEventListener('change', updateImageCardVisibility);
+ updateImageCardVisibility();
+ }
+
+ // ====== Image Upload via Media Library ======
+ const imageUploadBtn = document.getElementById('ka-cc-image-upload-btn');
+ const imageRemoveBtn = document.getElementById('ka-cc-image-remove-btn');
+ const imageUrlInput = document.getElementById('ka-cc-image-url');
+ const imagePreview = document.getElementById('ka-cc-image-preview');
+
+ if (imageUploadBtn) {
+ imageUploadBtn.addEventListener('click', function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (typeof wp === 'undefined' || !wp.media) {
+ alert('Media library not loaded. Please refresh the page.');
+ return;
+ }
+
+ const mediaFrame = wp.media({
+ title: '<?php echo esc_js(__('Select Cursor Image', 'king-addons')); ?>',
+ button: { text: '<?php echo esc_js(__('Use this image', 'king-addons')); ?>' },
+ multiple: false,
+ library: { type: 'image' }
+ });
+ mediaFrame.on('select', function() {
+ const attachment = mediaFrame.state().get('selection').first().toJSON();
+ if (imageUrlInput) imageUrlInput.value = attachment.url;
+ if (imagePreview) imagePreview.innerHTML = '<img src="' + attachment.url + '" style="max-width:100%;max-height:100%;object-fit:contain" />';
+ if (imageRemoveBtn) imageRemoveBtn.disabled = false;
+ updatePreview();
+ });
+ mediaFrame.open();
+ });
+ }
+ if (imageRemoveBtn) {
+ imageRemoveBtn.addEventListener('click', function(e) {
+ e.preventDefault();
+ imageUrlInput.value = '';
+ imagePreview.innerHTML = '<span class="dashicons dashicons-plus-alt2" style="color:#9ca3af;font-size:24px"></span>';
+ imageRemoveBtn.disabled = true;
+ updatePreview();
+ });
+ }
})();
</script>
<?php
@@ -1280,6 +1565,9 @@
$settings = $this->get_settings();
if ($this->should_activate(false, $settings)) {
$classes[] = 'ka-custom-cursor-enabled';
+ if (!empty($settings['hide_original_cursor'])) {
+ $classes[] = 'ka-cursor-hide-original';
+ }
}
return $classes;
}
@@ -1433,6 +1721,7 @@
$output['enabled'] = !empty($input['enabled']);
$output['disable_on_mobile'] = !empty($input['disable_on_mobile']);
+ $output['hide_original_cursor'] = !empty($input['hide_original_cursor']);
$output['rendering_mode'] = in_array($input['rendering_mode'] ?? 'css', ['css', 'enhanced'], true) ? $input['rendering_mode'] : 'css';
$preset = $input['preset'] ?? [];
@@ -1536,6 +1825,7 @@
return [
'enabled' => $enabled,
'mode' => $settings['rendering_mode'],
+ 'hideOriginalCursor' => !empty($settings['hide_original_cursor']),
'preset' => $settings['preset'],
'states' => $settings['states'],
'image' => $settings['image'],
@@ -1636,6 +1926,7 @@
return [
'enabled' => false,
'disable_on_mobile' => true,
+ 'hide_original_cursor' => true,
'rendering_mode' => 'css',
'preset' => [
'type' => 'dot',
--- a/king-addons/includes/extensions/Theme_Builder/Theme_Builder.php
+++ b/king-addons/includes/extensions/Theme_Builder/Theme_Builder.php
@@ -246,176 +246,293 @@
return;
}
- $this->handle_inline_actions();
+ // Keep cache fresh.
+ $this->repository->clear_cache();
+
+ // Shared dark theme support (used by Woo Builder admin page).
+ include KING_ADDONS_PATH . 'includes/admin/shared/dark-theme.php';
+ $this->handle_inline_actions();
$templates = $this->prepare_admin_templates();
- $table = new Admin_List_Table($templates, admin_url('admin.php?page=' . $this->menu_slug));
- $table->prepare_items();
- // Enqueue shared V3 styles
- wp_enqueue_style(
- 'king-addons-admin-v3',
- KING_ADDONS_URL . 'includes/admin/layouts/shared/admin-v3-styles.css',
- [],
- KING_ADDONS_VERSION
+ $base_url = admin_url('admin.php?page=' . $this->menu_slug);
+ $status_filter = isset($_GET['status']) ? sanitize_key(wp_unslash($_GET['status'])) : 'all'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if (!in_array($status_filter, ['all', 'enabled', 'disabled'], true)) {
+ $status_filter = 'all';
+ }
+ $filtered_templates = array_values(
+ array_filter(
+ $templates,
+ static function (array $template) use ($status_filter): bool {
+ if ('enabled' === $status_filter) {
+ return !empty($template['enabled']);
+ }
+ if ('disabled' === $status_filter) {
+ return empty($template['enabled']);
+ }
+ return true;
+ }
+ )
);
+ $type_cards = [
+ 'single' => [
+ 'label' => esc_html__('Single', 'king-addons'),
+ 'desc' => esc_html__('Posts, pages & custom post types', 'king-addons'),
+ 'default_location' => 'single_post',
+ ],
+ 'archive' => [
+ 'label' => esc_html__('Archive', 'king-addons'),
+ 'desc' => esc_html__('Blog, categories, tags & taxonomies', 'king-addons'),
+ 'default_location' => 'archive_blog',
+ ],
+ 'author' => [
+ 'label' => esc_html__('Author', 'king-addons'),
+ 'desc' => esc_html__('Author pages', 'king-addons'),
+ 'default_location' => 'author_all',
+ ],
+ 'search' => [
+ 'label' => esc_html__('Search', 'king-addons'),
+ 'desc' => esc_html__('Search results page', 'king-addons'),
+ 'default_location' => 'search_results',
+ ],
+ 'not_found' => [
+ 'label' => esc_html__('404', 'king-addons'),
+ 'desc' => esc_html__('Not found page', 'king-addons'),
+ 'default_location' => 'not_found',
+ ],
+ ];
+
$this->render_modern_styles();
- // Theme mode is per-user
- $theme_mode = get_user_meta(get_current_user_id(), 'king_addons_theme_mode', true);
- $allowed_theme_modes = ['dark', 'light', 'auto'];
- if (!in_array($theme_mode, $allowed_theme_modes, true)) {
- $theme_mode = 'dark';
- }
+ ka_render_dark_theme_styles();
+ ka_render_dark_theme_init();
?>
<script>
- (function() {
- const mode = '<?php echo esc_js($theme_mode); ?>';
- const mql = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
- const isDark = mode === 'auto' ? !!(mql && mql.matches) : mode === 'dark';
- document.documentElement.classList.toggle('ka-v3-dark', isDark);
+ if (document.body) {
document.body.classList.add('ka-admin-v3');
- document.body.classList.toggle('ka-v3-dark', isDark);
- })();
+ } else {
+ document.addEventListener('DOMContentLoaded', function() {
+ document.body.classList.add('ka-admin-v3');
+ });
+ }
</script>
- <div class="ka-admin-wrap ka-tb-wrap">
- <!-- Header -->
- <div class="ka-admin-header">
- <div class="ka-admin-header-left">
- <div class="ka-admin-header-icon">
- <span class="dashicons dashicons-layout"></span>
- </div>
- <div>
- <h1 class="ka-admin-title"><?php esc_html_e('Theme Builder', 'king-addons'); ?></h1>
- <p class="ka-admin-subtitle"><?php esc_html_e('Create custom templates for your site', 'king-addons'); ?></p>
+
+ <div class="ka-tb">
+ <header class="ka-tb-header">
+ <div class="ka-tb-header-content">
+ <span class="ka-tb-title-icon" aria-hidden="true">
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+ <path d="M4 4h16v16H4z" />
+ <path d="M8 8h8M8 12h8M8 16h5" />
+ </svg>
+ </span>
+ <div class="ka-tb-header-titles">
+ <h1><span class="ka-tb-title-text"><?php esc_html_e('Theme Builder', 'king-addons'); ?></span></h1>
+ <p><?php esc_html_e('Create custom templates for your site', 'king-addons'); ?></p>
</div>
</div>
- <div class="ka-admin-header-actions">
- <button type="button" id="ka-theme-builder-add-new" class="ka-btn ka-btn-primary">
- <span class="dashicons dashicons-plus-alt2"></span>
+ <div class="ka-tb-header-actions">
+ <button type="button" id="ka-theme-builder-add-new" class="ka-tb-btn ka-tb-btn-primary">
+ <span class="ka-tb-btn-icon" aria-hidden="true">+</span>
<?php esc_html_e('Add New Template', 'king-addons'); ?>
</button>
- <div class="ka-v3-segmented" id="ka-v3-theme-segment" role="radiogroup" aria-label="Theme" data-active="<?php echo esc_attr($theme_mode); ?>">
- <span class="ka-v3-segmented-indicator" aria-hidden="true"></span>
- <button type="button" class="ka-v3-segmented-btn" data-theme="light" aria-pressed="<?php echo $theme_mode === 'light' ? 'true' : 'false'; ?>"><?php esc_html_e('Light', 'king-addons'); ?></button>
- <button type="button" class="ka-v3-segmented-btn" data-theme="dark" aria-pressed="<?php echo $theme_mode === 'dark' ? 'true' : 'false'; ?>"><?php esc_html_e('Dark', 'king-addons'); ?></button>
- <button type="button" class="ka-v3-segmented-btn" data-theme="auto" aria-pressed="<?php echo $theme_mode === 'auto' ? 'true' : 'false'; ?>"><?php esc_html_e('Auto', 'king-addons'); ?></button>
- </div>
+ <?php ka_render_dark_theme_toggle(); ?>
</div>
- </div>
-
- <?php $this->render_add_new_modal(); ?>
- <?php $this->render_quick_edit_modal(); ?>
+ </header>
- <div class="ka-card">
- <div class="ka-card-header">
- <span class="dashicons dashicons-admin-page" style="color: #0071e3;"></span>
- <h2><?php esc_html_e('Templates', 'king-addons'); ?></h2>
- </div>
- <div class="ka-card-body" style="padding: 0;">
- <form method="post">
- <?php $table->views(); ?>
- <?php $table->display(); ?>
- </form>
- </div>
+ <div class="ka-tb-types" role="list">
+ <?php foreach ($type_cards as $type_slug => $data) : ?>
+ <?php $type_icon_svg = $this->get_theme_builder_type_icon_svg((string) $type_slug); ?>
+ <button
+ type="button"
+ class="ka-tb-type"
+ role="listitem"
+ data-ka-tb-type="<?php echo esc_attr($type_slug); ?>"
+ data-ka-tb-location="<?php echo esc_attr($data['default_location']); ?>"
+ >
+ <div class="ka-tb-type-icon" aria-hidden="true">
+ <?php echo $type_icon_svg; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </div>
+ <div class="ka-tb-type-label"><?php echo esc_html($data['label']); ?></div>
+ <div class="ka-tb-type-desc"><?php echo esc_html($data['desc']); ?></div>
+ </button>
+ <?php endforeach; ?>
</div>
- </div>
- <script>
- (function(