PluginProbe
Extendify / 3.2.0
Extendify v3.2.0
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
extendify / src / Agent / workflows / theme / components / SelectGeneratedPalette.jsx

SelectGeneratedPalette.jsx in Extendify 3.2.0, at src/Agent/workflows/theme/components/SelectGeneratedPalette.jsx

204 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useVariationOverride } from '@agent/hooks/useVariationOverride';
2 import { useChatStore } from '@agent/state/chat';
3 import { useEffect, useMemo, useRef, useState } from '@wordpress/element';
4 import { __ } from '@wordpress/i18n';
5
6 const colorsArrayToMap = (colors) => {
7 const map = {};
8 colors.forEach(({ slug, color }) => {
9 map[slug] = color;
10 });
11 return map;
12 };
13
14 const getBackgroundColor = (colors) => {
15 const bgSlugs = ['base', 'background', 'bg'];
16 for (const slug of bgSlugs) {
17 if (colors[slug]) return colors[slug];
18 }
19 return '#ffffff';
20 };
21
22 const buildPreviewCss = (colorsMap) => {
23 const styleEl = document.getElementById('global-styles-inline-css');
24 if (!styleEl) return '';
25 let css = styleEl.innerHTML;
26 Object.entries(colorsMap).forEach(([slug, hex]) => {
27 const regex = new RegExp(
28 `(--wp--preset--color--${slug}\\s*:\\s*)([^;]+)(;)`,
29 'g',
30 );
31 css = css.replace(regex, `$1${hex}$3`);
32 });
33 return css;
34 };
35
36 const themeDuotonePresets =
37 window.extAgentData?.context?.themePresets?.duotone || [];
38 const themeColorPresets =
39 window.extAgentData?.context?.themePresets?.colors || {};
40
41 const buildDuotoneTheme = (newColorsMap) => {
42 if (!themeDuotonePresets.length) return null;
43
44 const duotone = [];
45
46 for (const preset of themeDuotonePresets) {
47 const { slug, colors: originalColors } = preset;
48 if (!originalColors || originalColors.length !== 2) continue;
49
50 const newColors = originalColors.map((originalHex) => {
51 const matchingSlug = Object.entries(themeColorPresets).find(
52 ([, hex]) => hex.toLowerCase() === originalHex.toLowerCase(),
53 )?.[0];
54
55 if (matchingSlug && newColorsMap[matchingSlug]) {
56 return newColorsMap[matchingSlug];
57 }
58 return originalHex;
59 });
60
61 duotone.push({ slug, colors: newColors });
62 }
63
64 return duotone.length > 0 ? duotone : null;
65 };
66
67 export const SelectGeneratedPalette = ({
68 inputs,
69 onConfirm,
70 onCancel,
71 onRetry,
72 }) => {
73 const [selected, setSelected] = useState(null);
74 const [previewCss, setPreviewCss] = useState('');
75 const [duotoneTheme, setDuotoneTheme] = useState(null);
76 const { addMessage, messages } = useChatStore();
77 const aiPalettes = inputs?.palettes;
78
79 const palettes = useMemo(
80 () =>
81 aiPalettes?.map(({ name, colors }) => {
82 return {
83 name,
84 colors: colorsArrayToMap(colors),
85 colorsArray: colors,
86 };
87 }) || [],
88 [aiPalettes],
89 );
90 const noPalettes = !aiPalettes?.length || palettes.length === 0;
91
92 const { undoChange } = useVariationOverride({
93 css: previewCss,
94 duotoneTheme,
95 });
96
97 const confirmed = useRef(false);
98 useEffect(() => {
99 return () => {
100 if (!confirmed.current) undoChange();
101 };
102 }, []);
103
104 useEffect(() => {
105 if (!noPalettes) return;
106 const timer = setTimeout(() => onCancel(), 100);
107 const content = __(
108 'We were unable to generate color palettes. Please try again.',
109 'extendify-local',
110 );
111 const last = messages.at(-1)?.details?.content;
112 if (content === last) return () => clearTimeout(timer);
113 addMessage('message', { role: 'assistant', content, error: true });
114 return () => clearTimeout(timer);
115 }, [addMessage, onCancel, noPalettes, messages]);
116
117 const handleRetry = () => {
118 undoChange();
119 onRetry();
120 };
121
122 const handleConfirm = () => {
123 if (!selected) return;
124 confirmed.current = true;
125 const palette = palettes.find((p) => p.name === selected);
126 onConfirm({
127 data: {
128 palette: {
129 name: palette.name,
130 colors: palette.colorsArray.map(({ slug, color }) => ({
131 slug,
132 color,
133 name: slug,
134 })),
135 },
136 duotone: duotoneTheme,
137 },
138 shouldRefreshPage: true,
139 });
140 };
141
142 if (noPalettes) return null;
143
144 return (
145 <div className="mb-4 ms-12 me-2 flex flex-col rounded-lg border border-gray-300 bg-gray-50">
146 <div className="rounded-lg border-b border-gray-300 bg-white">
147 <div className="grid grid-cols-2 gap-2 p-3">
148 {palettes.map(({ name, colors, colorsArray }) => (
149 <button
150 key={name}
151 type="button"
152 style={{ backgroundColor: getBackgroundColor(colors) }}
153 className={`relative flex w-full items-center justify-center overflow-hidden rounded-lg border border-gray-300 p-2 text-center text-sm ${
154 selected === name ? 'ring ring-design-main ring-wp' : ''
155 }`}
156 onClick={() => {
157 setSelected(name);
158 setPreviewCss(buildPreviewCss(colors));
159 setDuotoneTheme(buildDuotoneTheme(colors));
160 }}
161 >
162 <div className="flex max-w-fit items-center justify-center -space-x-4 rounded-lg rtl:space-x-reverse">
163 {colorsArray
164 .filter(({ slug }) => slug !== 'background')
165 .map(({ slug, color }) => (
166 <div
167 key={slug}
168 style={{ backgroundColor: color }}
169 className="size-6 shrink-0 overflow-visible rounded-full border border-white md:size-7"
170 />
171 ))}
172 </div>
173 </button>
174 ))}
175 </div>
176 </div>
177 <div className="flex justify-start gap-2 p-3">
178 <button
179 type="button"
180 className="w-full rounded-sm border border-gray-500 bg-white p-2 text-sm text-gray-900"
181 onClick={onCancel}
182 >
183 {__('Cancel', 'extendify-local')}
184 </button>
185 <button
186 type="button"
187 className="w-full rounded-sm border border-gray-500 bg-white p-2 text-sm text-gray-900"
188 onClick={handleRetry}
189 >
190 {__('Try Again', 'extendify-local')}
191 </button>
192 <button
193 type="button"
194 className="w-full rounded-sm border border-design-main bg-design-main p-2 text-sm text-white"
195 disabled={!selected}
196 onClick={handleConfirm}
197 >
198 {__('Save', 'extendify-local')}
199 </button>
200 </div>
201 </div>
202 );
203 };
204