PluginProbe
Extendify / 3.2.1
Extendify v3.2.1
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
← All changes | app/Agent/Admin.php +106 -6 3.1.03.2.1 View file →
@@ -15,11 +15,14 @@
15 15 use Extendify\Shared\Services\Escaper;
16 16 use Extendify\Shared\Services\HttpClient;
17 17 use Extendify\Agent\TagBlocks;
18 18 use Extendify\Agent\TagTemplateParts;
19 +use Extendify\Agent\WooProductImages;
20 +use Extendify\Agent\AbilitiesDiscovery;
19 21 use Extendify\Agent\Controllers\SiteNavigationController;
20 22 use Extendify\PartnerData;
21 23 use Extendify\Shared\DataProvider\ProductsData;
24 +use Extendify\SiteVisibility;
22 25
23 26 /**
24 27 * This class handles any file loading for the admin area.
25 28 */
@@ -42,12 +45,30 @@
42 45
43 46 // Add the site navigation ids to the navigation blocks
44 47 SiteNavigationController::init();
45 48
49 + Skeleton::init();
50 +
51 + WooProductImages::init();
52 +
46 53 \add_action('extendify_agent_suggestions_refresh', [$this, 'refreshSuggestions']);
47 54 }
48 55
49 56 /**
57 + * Where the agent panel mounts — docked-left only on the onboarding front end.
58 + *
59 + * @return string
60 + */
61 + public static function agentPosition()
62 + {
63 + $agentOnboarding = PartnerData::setting('useAgentOnboarding') ||
64 + Config::preview('agent-onboarding') ||
65 + constant('EXTENDIFY_DEVMODE');
66 +
67 + return $agentOnboarding && !is_admin() ? 'docked-left' : 'floating';
68 + }
69 +
70 + /**
50 71 * Adds various JS scripts and styles
51 72 *
52 73 * @return void
53 74 */
@@ -98,14 +119,13 @@
98 119 (bool) use_block_editor_for_post($this->getCurrentPostId()) :
99 120 false,
100 121 'isOnEditorOrFSE' => $this->isGutenbergOrFse(),
101 122 'activePlugins' => array_values(\get_option('active_plugins', [])),
102 - // Whether the user is using the vibes experience or not.
103 - 'isUsingVibes' => (bool) file_exists(EXTENDIFY_PATH . 'src/Launch/_data/block-style-variations.json') &&
104 - version_compare(wp_get_theme("extendable")->get('Version'), '2.0.32', '>='),
123 + 'isUsingVibes' => version_compare((string) wp_get_theme('extendable')->get('Version'), '2.0.32', '>='),
105 124 'siteTitle' => \esc_attr(\get_bloginfo('name')),
106 125 'siteDescription' => \esc_attr(\get_bloginfo('description')),
107 126 'themePresets' => $this->getThemePresets(),
127 + 'presetSlugs' => $this->getPresetSlugs(),
108 128 ];
109 129 $recommendations = ProductsData::get() ?? [];
110 130 $pluginRecommendations = array_filter($recommendations, function ($item) {
111 131 return in_array('ai-agent', $item['slots'] ?? [], true) && $item['ctaType'] === 'plugin';
@@ -120,8 +140,10 @@
120 140 }, $pluginRecommendations));
121 141 $agentContext = [
122 142 'availableAdminPages' => get_option('_transient_extendify_admin_pages_menu', []),
123 143 'pluginRecommendations' => $mappedPluginRecommendations,
144 + 'sitePublished' => SiteVisibility::isPublished(),
145 + 'comingSoonEnabled' => (bool) PartnerData::setting('useComingSoon'),
124 146 ];
125 147 $abilities = [
126 148 'canEditPost' => (bool) \current_user_can('edit_post', \get_queried_object_id()),
127 149 // TODO: this may be true for a user, while they still can't edit every post
@@ -144,17 +166,18 @@
144 166 Config::$slug . '-agent-scripts',
145 167 'window.extAgentData = ' . \wp_json_encode([
146 168 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
147 169 'startOnboarding' => isset($_GET['extendify-launch-success']) && $agentOnboarding,
148 - 'agentPosition' => $agentOnboarding && !is_admin() ? 'docked-left' : 'floating',
170 + 'agentPosition' => self::agentPosition(),
149 171 // Add context about where they are
150 172 'context' => $context,
151 - // Context that the Agent might need when returning a response,
152 - // but not for handling the workflow.
173 + // Material a workflow may want; each declares the keys it reads.
153 174 'agentContext' => $agentContext,
154 175 // List of abilities the AI can perform for this user.
155 176 // For example, we could check whether their theme has variations.
156 177 'abilities' => $abilities,
178 + // Registered WordPress Abilities (6.9+) this user may run.
179 + 'wpAbilities' => AbilitiesDiscovery::discover(),
157 180 // List of suggestions the AI can make for this user.
158 181 // For example, we could check whether they need to set up a specific plugin.
159 182 'suggestions' => $this->getSuggestions(),
160 183 'domainsSuggestionSettings' => [
@@ -272,8 +295,85 @@
272 295 'fontSizes' => $fontSizes,
273 296 'fontFamilies' => $fontFamilies,
274 297 'colorPairs' => $colorPairs,
275 298 ];
299 + }
300 +
301 + /**
302 + * Preset slugs per design-token family across every origin. Feeds the Agent's
303 + * block-attribute schema so the model picks named tokens, not raw CSS.
304 + *
305 + * @return array<string,string[]>
306 + */
307 + private function getPresetSlugs()
308 + {
309 + if (!function_exists('wp_get_global_settings')) {
310 + return [];
311 + }
312 +
313 + $color = \wp_get_global_settings(['color']);
314 + $typography = \wp_get_global_settings(['typography']);
315 +
316 + return [
317 + 'color' => $this->originSlugs($color, 'palette', 'defaultPalette'),
318 + 'colorValues' => $this->originValues($color, 'palette', 'defaultPalette', 'color'),
319 + 'gradient' => $this->originSlugs($color, 'gradients', 'defaultGradients'),
320 + 'fontSize' => $this->originSlugs($typography, 'fontSizes', 'defaultFontSizes'),
321 + 'fontFamily' => $this->originSlugs($typography, 'fontFamilies', 'defaultFontFamilies'),
322 + ];
323 + }
324 +
325 + /**
326 + * Slug => value, so a colour written as its own hex finds the token naming it.
327 + *
328 + * @return array<string, string>
329 + */
330 + private function originValues($node, $listKey, $defaultKey, $valueKey)
331 + {
332 + $list = $node[$listKey] ?? [];
333 +
334 + $origins = ['custom', 'theme'];
335 + if (($node[$defaultKey] ?? true) !== false) {
336 + $origins[] = 'default';
337 + }
338 +
339 + $values = [];
340 + foreach ($origins as $origin) {
341 + foreach ($list[$origin] ?? [] as $item) {
342 + if (isset($item['slug'], $item[$valueKey])) {
343 + $values[(string) $item['slug']] = (string) $item[$valueKey];
344 + }
345 + }
346 + }
347 +
348 + return $values;
349 + }
350 +
351 + /**
352 + * Preset slugs from a settings node's origin buckets; drops the `default`
353 + * origin when the theme opted the family out.
354 + *
355 + * @return string[]
356 + */
357 + private function originSlugs($node, $listKey, $defaultKey)
358 + {
359 + $list = $node[$listKey] ?? [];
360 +
361 + $origins = ['custom', 'theme'];
362 + if (($node[$defaultKey] ?? true) !== false) {
363 + $origins[] = 'default';
364 + }
365 +
366 + $slugs = [];
367 + foreach ($origins as $origin) {
368 + foreach ($list[$origin] ?? [] as $item) {
369 + if (isset($item['slug'])) {
370 + $slugs[] = (string) $item['slug'];
371 + }
372 + }
373 + }
374 +
375 + return array_values(array_unique($slugs));
276 376 }
277 377
278 378 private static function extractColorSlug(string $value)
279 379 {