PluginProbe
Extendify / trunk
Extendify vtrunk
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.1 → trunk 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 {