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 | src/AutoLaunch/fetchers/get-design-build.js +65 -46 3.1.3 → trunk View file →
@@ -1,6 +1,6 @@
1 -import { uploadLogo } from '@auto-launch/fetchers/get-logo';
2 -import { getThemeVariation } from '@auto-launch/fetchers/get-variation';
1 +import { isExternalLogo, uploadLogo } from '@auto-launch/fetchers/get-logo';
2 +import { getStyleDocument } from '@auto-launch/fetchers/get-style-document';
3 3 import {
4 4 getDesignBuildShape,
5 5 getLogoShape,
6 6 getPluginsShape,
@@ -5,8 +5,9 @@
5 5 getLogoShape,
6 6 getPluginsShape,
7 7 getStyleShape,
8 8 } from '@auto-launch/fetchers/shape';
9 +import { importBuiltPagesImages } from '@auto-launch/functions/get-imported-images';
9 10 import {
10 11 fetchWithTimeout,
11 12 retryTwice,
12 13 setStatus,
@@ -12,22 +13,28 @@
12 13 setStatus,
13 14 } from '@auto-launch/functions/helpers';
14 15 import { updateOption } from '@auto-launch/functions/wp';
15 16 import { useLaunchDataStore } from '@auto-launch/state/launch-data';
17 +import { launchStrings } from '@auto-launch/strings';
16 18 import { AI_HOST } from '@constants';
17 19 import { digest } from '@shared/api/digest';
18 -import { __ } from '@wordpress/i18n';
19 20 import { mutate } from 'swr';
20 21
21 22 const fallback = null;
22 23 const headers = { 'Content-Type': 'application/json' };
23 24
25 +// Left set, the launch screen waits forever on a design that never arrives.
26 +const dropBuildId = () =>
27 + useLaunchDataStore.setState((s) => ({
28 + urlParams: { ...s.urlParams, 'build-id': '' },
29 + }));
30 +
24 31 export const handleDesignBuild = async ({ urlParams }) => {
25 32 const buildId = urlParams?.['build-id'];
26 33 if (!buildId) return fallback;
27 34
28 35 // translators: this is for a action log UI. Keep it short
29 - setStatus(__('Loading your design', 'extendify-local'));
36 + setStatus(launchStrings().statusDesign);
30 37
31 38 const url = `${AI_HOST}/api/design/${encodeURIComponent(buildId)}`;
32 39 const response = await retryTwice(() =>
33 40 fetchWithTimeout(url, { headers }),
@@ -43,8 +50,9 @@
43 50 status: response.status,
44 51 },
45 52 details: { source: 'auto-launch', caller: 'handleDesignBuild' },
46 53 });
54 + dropBuildId();
47 55 return fallback;
48 56 }
49 57
50 58 try {
@@ -54,17 +62,16 @@
54 62 const profile = parsed.siteProfile;
55 63 await updateOption('extendify_site_profile', JSON.stringify(profile));
56 64 mutate('siteProfile', { siteProfile: profile }, false);
57 65
58 - // Stash the site style and variation
59 66 const style = parsed.siteStyle;
60 - const fonts =
61 - style.fonts?.heading || style.fonts?.body ? style.fonts : null;
62 - const variation = await getThemeVariation(
63 - { slug: style.colorPalette, fonts },
64 - { fallback: true },
65 - );
67 + const variation = await getStyleDocument({
68 + colorPalette: style.colorPalette,
69 + fonts: style.fonts,
70 + });
66 71 const siteStyle = { ...style, variation };
72 + // The Agent reads extendify_siteStyle; the legacy row keeps old readers.
73 + await updateOption('extendify_siteStyle', siteStyle);
67 74 await updateOption('extendify_site_style', siteStyle);
68 75 await updateOption('extendify_animation_settings', {
69 76 type: style.animation ?? 'fade',
70 77 speed: 'medium',
@@ -74,15 +81,39 @@
74 81 // Stash the plugins
75 82 const sitePlugins = parsed.selectedPlugins;
76 83 mutate('sitePlugins', getPluginsShape.parse({ sitePlugins }), false);
77 84
78 - // Stash the logo
79 - await uploadLogo(parsed.logoUrl);
80 - mutate('siteLogo', getLogoShape.parse({ logoUrl: parsed.logoUrl }), false);
85 + // The logo upload swaps logoUrl for a Media Library URL without the
86 + // logos-custom/ marker, so capture "is the brand's own logo" from the
87 + // source URL now.
88 + const hasExternalLogo = isExternalLogo(parsed.logoUrl);
81 89
82 - const designBuild = { buildId, ...parsed, siteProfile: profile, siteStyle };
83 - // Spreading it here sets it for other state values we override
84 - return { designBuild, ...designBuild };
90 + // Sideload the built-page images, and upload the logo alongside when the
91 + // build provided one. logoUrl is nullable; without it we skip the upload
92 + // so the siteLogo step falls through to AI logo generation.
93 + const uploads = [importBuiltPagesImages(parsed.builtPages)];
94 + if (parsed.logoUrl) {
95 + mutate(
96 + 'siteLogo',
97 + getLogoShape.parse({ logoUrl: parsed.logoUrl }),
98 + false,
99 + );
100 + uploads.push(uploadLogo(parsed.logoUrl, { external: hasExternalLogo }));
101 + }
102 + const [builtPages] = await Promise.all(uploads);
103 +
104 + const designBuild = {
105 + buildId,
106 + ...parsed,
107 + builtPages,
108 + hasExternalLogo,
109 + siteProfile: profile,
110 + siteStyle,
111 + };
112 + // Exclude `pages` from the spread: the pages step owns it (templates with
113 + // patterns); designBuild.pages (slug/name only) would clobber it.
114 + const { pages, ...topLevel } = designBuild;
115 + return { designBuild, ...topLevel };
85 116 } catch (e) {
86 117 digest({
87 118 error: e,
88 119 details: { source: 'auto-launch', caller: 'handleDesignBuild::parsing' },
@@ -87,30 +118,32 @@
87 118 error: e,
88 119 details: { source: 'auto-launch', caller: 'handleDesignBuild::parsing' },
89 120 });
90 121 console.error('handleDesignBuild:', e);
91 - // Drop the build-id so downstream checks (e.g. skipDescription) fallback
92 - useLaunchDataStore.setState((s) => ({
93 - urlParams: { ...s.urlParams, 'build-id': '' },
94 - }));
122 + dropBuildId();
95 123 return fallback;
96 124 }
97 125 };
98 126
99 -// Prepend the design build hero; flagged so it skips content regeneration.
127 +// Prepend the design build's hero to the fetched home template. Full-page
128 +// builds skip /api/home entirely and are assembled in handleHome.
100 129 export const applyDesignBuildHero = (patterns, designBuild) => {
101 - if (!designBuild?.patternCode) return patterns;
102 - return [
103 - {
104 - name: designBuild.patternId,
105 - code: designBuild.patternCode,
106 - patternTypes: ['hero-header'],
107 - contentGenerated: true,
108 - },
109 - ...patterns,
110 - ];
130 + const builtHome = designBuild?.builtPages?.find((p) => p.slug === 'home');
131 + if (!builtHome?.patterns?.length) return patterns;
132 + return [...builtHome.patterns, ...patterns];
111 133 };
112 134
135 +// /api/home answers a locked nav with one section per entry, in order, and holds back
136 +// the hero, so a count mismatch means that broke: leave the names to the pattern types.
137 +export const applyDesignBuildNav = (patterns, designBuild, structure) => {
138 + const pages = designBuild?.pages ?? [];
139 + if (structure !== 'single-page' || patterns.length !== pages.length) {
140 + return patterns;
141 + }
142 +
143 + return patterns.map((pattern, i) => ({ ...pattern, navSlug: pages[i].slug }));
144 +};
145 +
113 146 // Reorder pages to match the design build's page order; extras fall to the end.
114 147 export const applyDesignBuildOrder = (pages, designBuild) => {
115 148 const order = designBuild?.pages?.map((p) => p.slug) ?? [];
116 149 if (!order.length) return pages;
@@ -117,19 +150,5 @@
117 150 return [
118 151 ...order.map((slug) => pages.find((t) => t.slug === slug)).filter(Boolean),
119 152 ...pages.filter((t) => !order.includes(t.slug)),
120 153 ];
121 -};
122 -
123 -// Tag non-hero patterns with their aligned design build page slug/name.
124 -// For single-page sites mainly
125 -export const applyDesignBuildNav = (patterns, designBuild) => {
126 - const pages = designBuild?.pages ?? [];
127 - if (!pages.length) return patterns;
128 - let i = 0;
129 - return patterns.map((pattern) => {
130 - if (pattern.patternTypes?.includes('hero-header')) return pattern;
131 - const page = pages[i++];
132 - if (!page) return pattern;
133 - return { ...pattern, navSlug: page.slug, navLabel: page.name };
134 - });
135 154 };