slice-machine-ui 2.19.2-alpha.jp-figma-to-prismic.1 → 2.19.2-alpha.jp-figma-to-prismic.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/404.html +1 -1
- package/out/_next/static/{dTxDQVqdse1UxnwBqviBt → 8UM-Fh6SBL5e0B_msyLxt}/_buildManifest.js +1 -1
- package/out/_next/static/chunks/20-4cb8941c8aafa019.js +1 -0
- package/out/_next/static/chunks/593-97393b59cba3d429.js +1 -0
- package/out/_next/static/chunks/907-fbd4308471792876.js +1 -0
- package/out/_next/static/chunks/pages/{_app-45762da0ab33fb54.js → _app-a9ca8c9f371bd423.js} +1 -1
- package/out/_next/static/chunks/pages/{changes-7f24b37f5bf872ae.js → changes-b3f45dfeb5dc08f0.js} +1 -1
- package/out/_next/static/chunks/pages/custom-types/{[customTypeId]-1b47424a37b49dff.js → [customTypeId]-02278526092bcf5c.js} +1 -1
- package/out/_next/static/chunks/pages/page-types/{[pageTypeId]-385f933c203e8b16.js → [pageTypeId]-4d99de1b52de7c9b.js} +1 -1
- package/out/_next/static/chunks/pages/slices/[lib]/[sliceName]/{[variation]-08cdeefc96106c0c.js → [variation]-330e5d545d9f6269.js} +1 -1
- package/out/_next/static/chunks/pages/{slices-bedcb854fbdca8cd.js → slices-de5f8cf4719e88c4.js} +1 -1
- package/out/changelog.html +1 -1
- package/out/changes.html +1 -1
- package/out/custom-types/[customTypeId].html +1 -1
- package/out/custom-types.html +1 -1
- package/out/index.html +1 -1
- package/out/labs.html +1 -1
- package/out/page-types/[pageTypeId].html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation]/simulator.html +1 -1
- package/out/slices/[lib]/[sliceName]/[variation].html +1 -1
- package/out/slices.html +1 -1
- package/package.json +3 -3
- package/src/features/customTypes/customTypesBuilder/CreateSliceFromImageModal/CreateSliceFromImageModal.tsx +362 -198
- package/src/features/customTypes/customTypesBuilder/CreateSliceFromImageModal/SliceCard.tsx +43 -11
- package/src/icons/FigmaIcon.tsx +78 -34
- package/src/legacy/components/ScreenshotChangesModal/index.tsx +1 -1
- package/test/src/modules/__fixtures__/serverState.ts +2 -0
- package/out/_next/static/chunks/34-28725deef8b874b1.js +0 -1
- package/out/_next/static/chunks/658-8231c0b729e0124a.js +0 -1
- package/out/_next/static/chunks/907-0225b94cf58be87d.js +0 -1
- /package/out/_next/static/{dTxDQVqdse1UxnwBqviBt → 8UM-Fh6SBL5e0B_msyLxt}/_ssgManifest.js +0 -0
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BlankSlate,
|
|
3
|
-
BlankSlateActions,
|
|
4
|
-
BlankSlateDescription,
|
|
5
3
|
BlankSlateIcon,
|
|
6
|
-
BlankSlateTitle,
|
|
7
4
|
Box,
|
|
8
5
|
Button,
|
|
9
6
|
Dialog,
|
|
@@ -16,6 +13,7 @@ import {
|
|
|
16
13
|
FileDropZone,
|
|
17
14
|
FileUploadButton,
|
|
18
15
|
ScrollArea,
|
|
16
|
+
Text,
|
|
19
17
|
} from "@prismicio/editor-ui";
|
|
20
18
|
import { SharedSlice } from "@prismicio/types-internal/lib/customtypes";
|
|
21
19
|
import { useEffect, useRef, useState } from "react";
|
|
@@ -27,6 +25,7 @@ import { getState, telemetry } from "@/apiClient";
|
|
|
27
25
|
import { addAiFeedback } from "@/features/aiFeedback";
|
|
28
26
|
import { useOnboarding } from "@/features/onboarding/useOnboarding";
|
|
29
27
|
import { useAutoSync } from "@/features/sync/AutoSyncProvider";
|
|
28
|
+
import { FigmaIcon } from "@/icons/FigmaIcon";
|
|
30
29
|
import { managerClient } from "@/managerClient";
|
|
31
30
|
import useSliceMachineActions from "@/modules/useSliceMachineActions";
|
|
32
31
|
|
|
@@ -56,12 +55,12 @@ interface CreateSliceFromImageModalProps {
|
|
|
56
55
|
export function CreateSliceFromImageModal(
|
|
57
56
|
props: CreateSliceFromImageModalProps,
|
|
58
57
|
) {
|
|
59
|
-
const { open, location,
|
|
58
|
+
const { open, location, onClose, onSuccess } = props;
|
|
60
59
|
const [slices, setSlices] = useState<Slice[]>([]);
|
|
61
|
-
const [isCreatingSlices, setIsCreatingSlices] = useState(false);
|
|
62
60
|
const { syncChanges } = useAutoSync();
|
|
63
61
|
const { createSliceSuccess } = useSliceMachineActions();
|
|
64
62
|
const { completeStep } = useOnboarding();
|
|
63
|
+
const existingSlices = useExistingSlices({ open });
|
|
65
64
|
|
|
66
65
|
/**
|
|
67
66
|
* Keeps track of the current instance id.
|
|
@@ -69,6 +68,21 @@ export function CreateSliceFromImageModal(
|
|
|
69
68
|
*/
|
|
70
69
|
const id = useRef(crypto.randomUUID());
|
|
71
70
|
|
|
71
|
+
useHotkeys(
|
|
72
|
+
["meta+v", "ctrl+v"],
|
|
73
|
+
(event) => {
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
void handlePaste();
|
|
76
|
+
},
|
|
77
|
+
{ enabled: open },
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (slices.every((slice) => slice.status === "success")) {
|
|
82
|
+
void onAllComplete();
|
|
83
|
+
}
|
|
84
|
+
}, [slices]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
85
|
+
|
|
72
86
|
const setSlice = (args: {
|
|
73
87
|
index: number;
|
|
74
88
|
slice: (prevSlice: Slice) => Slice;
|
|
@@ -78,7 +92,7 @@ export function CreateSliceFromImageModal(
|
|
|
78
92
|
};
|
|
79
93
|
|
|
80
94
|
const onOpenChange = (open: boolean) => {
|
|
81
|
-
if (open
|
|
95
|
+
if (open) return;
|
|
82
96
|
onClose();
|
|
83
97
|
id.current = crypto.randomUUID();
|
|
84
98
|
setSlices([]);
|
|
@@ -92,17 +106,33 @@ export function CreateSliceFromImageModal(
|
|
|
92
106
|
return;
|
|
93
107
|
}
|
|
94
108
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
const startIndex = slices.length;
|
|
110
|
+
setSlices((prevSlices) => [
|
|
111
|
+
...prevSlices,
|
|
112
|
+
...images.map(
|
|
113
|
+
(image): Slice => ({
|
|
114
|
+
status: "uploading",
|
|
115
|
+
source: "upload",
|
|
116
|
+
image,
|
|
117
|
+
}),
|
|
118
|
+
),
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
images.forEach((imageData, relativeIndex) => {
|
|
122
|
+
const index = startIndex + relativeIndex;
|
|
123
|
+
void uploadImage({ index, imageData, source: "upload" });
|
|
124
|
+
});
|
|
103
125
|
};
|
|
104
126
|
|
|
105
127
|
const handlePaste = async () => {
|
|
128
|
+
if (!open) return;
|
|
129
|
+
|
|
130
|
+
// Don't allow pasting while uploads or generation are in progress
|
|
131
|
+
const isLoading = slices.some(
|
|
132
|
+
(slice) => slice.status === "uploading" || slice.status === "generating",
|
|
133
|
+
);
|
|
134
|
+
if (isLoading) return;
|
|
135
|
+
|
|
106
136
|
const supportsClipboardRead =
|
|
107
137
|
typeof navigator.clipboard?.read === "function";
|
|
108
138
|
|
|
@@ -124,12 +154,9 @@ export function CreateSliceFromImageModal(
|
|
|
124
154
|
|
|
125
155
|
// Method 1: Try to extract image from clipboard image/png blob (preferred)
|
|
126
156
|
for (const item of clipboardItems) {
|
|
127
|
-
console.log("Clipboard item types:", item.types);
|
|
128
|
-
|
|
129
157
|
const imageType = item.types.find((type) => type.startsWith("image/"));
|
|
130
158
|
if (imageType !== undefined) {
|
|
131
159
|
imageBlob = await item.getType(imageType);
|
|
132
|
-
console.log("Extracted image from clipboard image type:", imageType);
|
|
133
160
|
break;
|
|
134
161
|
}
|
|
135
162
|
}
|
|
@@ -146,14 +173,11 @@ export function CreateSliceFromImageModal(
|
|
|
146
173
|
success = true;
|
|
147
174
|
const data = result.data;
|
|
148
175
|
imageName = `${data.name}.png`;
|
|
149
|
-
console.log("Extracted name from text/plain JSON:", data);
|
|
150
176
|
|
|
151
177
|
// Use base64 image as fallback if no blob was found
|
|
152
178
|
if (!imageBlob) {
|
|
153
179
|
const response = await fetch(data.image);
|
|
154
180
|
imageBlob = await response.blob();
|
|
155
|
-
console.log("Extracted image from base64 fallback");
|
|
156
|
-
console.log("Image blob type:", imageBlob.type);
|
|
157
181
|
}
|
|
158
182
|
} else {
|
|
159
183
|
console.warn("Clipboard data validation failed:", result.error);
|
|
@@ -168,11 +192,11 @@ export function CreateSliceFromImageModal(
|
|
|
168
192
|
if (!imageBlob) {
|
|
169
193
|
if (success) {
|
|
170
194
|
toast.error(
|
|
171
|
-
"
|
|
195
|
+
"Could not extract Figma data from clipboard. Please try copying again using the Prismic Figma plugin.",
|
|
172
196
|
);
|
|
173
197
|
} else {
|
|
174
198
|
toast.error(
|
|
175
|
-
"No Figma data found in clipboard. Make sure you've copied a
|
|
199
|
+
"No Figma data found in clipboard. Make sure you've copied a design using the Prismic Figma plugin.",
|
|
176
200
|
);
|
|
177
201
|
}
|
|
178
202
|
return;
|
|
@@ -188,20 +212,23 @@ export function CreateSliceFromImageModal(
|
|
|
188
212
|
}
|
|
189
213
|
|
|
190
214
|
// Create File object from blob and append to existing slices
|
|
191
|
-
const
|
|
215
|
+
const imageData = new File([imageBlob], imageName, {
|
|
216
|
+
type: imageBlob.type,
|
|
217
|
+
});
|
|
192
218
|
const newIndex = currentSliceCount;
|
|
193
219
|
|
|
194
220
|
// Append new slice to existing ones
|
|
195
221
|
setSlices((prevSlices) => [
|
|
196
222
|
...prevSlices,
|
|
197
223
|
{
|
|
224
|
+
source: "figma",
|
|
198
225
|
status: "uploading",
|
|
199
|
-
image:
|
|
226
|
+
image: imageData,
|
|
200
227
|
},
|
|
201
228
|
]);
|
|
202
229
|
|
|
203
230
|
// Start uploading the new image
|
|
204
|
-
uploadImage({ index: newIndex,
|
|
231
|
+
void uploadImage({ index: newIndex, imageData, source: "figma" });
|
|
205
232
|
|
|
206
233
|
toast.success(`Pasted ${imageName}${success ? " from Figma" : ""}`);
|
|
207
234
|
} catch (error) {
|
|
@@ -212,18 +239,12 @@ export function CreateSliceFromImageModal(
|
|
|
212
239
|
}
|
|
213
240
|
};
|
|
214
241
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
},
|
|
222
|
-
{ enabled: open },
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
const uploadImage = (args: { index: number; image: File }) => {
|
|
226
|
-
const { index, image } = args;
|
|
242
|
+
const uploadImage = async (args: {
|
|
243
|
+
index: number;
|
|
244
|
+
imageData: File;
|
|
245
|
+
source: "figma" | "upload";
|
|
246
|
+
}) => {
|
|
247
|
+
const { index, imageData, source } = args;
|
|
227
248
|
const currentId = id.current;
|
|
228
249
|
|
|
229
250
|
setSlice({
|
|
@@ -231,32 +252,63 @@ export function CreateSliceFromImageModal(
|
|
|
231
252
|
slice: (prevSlice) => ({
|
|
232
253
|
...prevSlice,
|
|
233
254
|
status: "uploading",
|
|
255
|
+
image: imageData,
|
|
256
|
+
source,
|
|
234
257
|
}),
|
|
235
258
|
});
|
|
236
259
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
260
|
+
try {
|
|
261
|
+
const imageUrl = await getImageUrl({ image: imageData });
|
|
262
|
+
if (currentId !== id.current) return;
|
|
263
|
+
|
|
264
|
+
setSlice({
|
|
265
|
+
index,
|
|
266
|
+
slice: (prevSlice) => ({
|
|
267
|
+
...prevSlice,
|
|
268
|
+
status: "pending",
|
|
269
|
+
thumbnailUrl: imageUrl,
|
|
270
|
+
}),
|
|
271
|
+
});
|
|
272
|
+
} catch {
|
|
273
|
+
if (currentId !== id.current) return;
|
|
274
|
+
setSlice({
|
|
275
|
+
index,
|
|
276
|
+
slice: (prevSlice) => ({
|
|
277
|
+
...prevSlice,
|
|
278
|
+
status: "uploadError",
|
|
279
|
+
onRetry: () => void uploadImage({ index, imageData, source }),
|
|
280
|
+
}),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const generateAllPendingSlices = async () => {
|
|
286
|
+
const smConfig = await managerClient.project.getSliceMachineConfig();
|
|
287
|
+
const libraryID = smConfig?.libraries?.[0];
|
|
288
|
+
if (libraryID === undefined) {
|
|
289
|
+
throw new Error("No library found in the config.");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Generate all pending slices simultaneously
|
|
293
|
+
slices.forEach((slice, index) => {
|
|
294
|
+
if (slice.status === "pending") {
|
|
295
|
+
void inferSlice({
|
|
245
296
|
index,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
onRetry: () => uploadImage({ index, image }),
|
|
250
|
-
}),
|
|
297
|
+
imageUrl: slice.thumbnailUrl,
|
|
298
|
+
libraryID,
|
|
299
|
+
source: slice.source,
|
|
251
300
|
});
|
|
252
|
-
}
|
|
253
|
-
);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
254
303
|
};
|
|
255
304
|
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
305
|
+
const inferSlice = async (args: {
|
|
306
|
+
index: number;
|
|
307
|
+
imageUrl: string;
|
|
308
|
+
libraryID: string;
|
|
309
|
+
source: "figma" | "upload";
|
|
310
|
+
}) => {
|
|
311
|
+
const { index, imageUrl, libraryID, source } = args;
|
|
260
312
|
const currentId = id.current;
|
|
261
313
|
|
|
262
314
|
setSlice({
|
|
@@ -268,110 +320,190 @@ export function CreateSliceFromImageModal(
|
|
|
268
320
|
}),
|
|
269
321
|
});
|
|
270
322
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
newSlices: prevSlices,
|
|
285
|
-
slice,
|
|
286
|
-
}),
|
|
287
|
-
langSmithUrl,
|
|
288
|
-
}
|
|
289
|
-
: prevSlice,
|
|
290
|
-
),
|
|
291
|
-
);
|
|
292
|
-
},
|
|
293
|
-
() => {
|
|
294
|
-
if (currentId !== id.current) return;
|
|
295
|
-
setSlice({
|
|
296
|
-
index,
|
|
297
|
-
slice: (prevSlice) => ({
|
|
323
|
+
try {
|
|
324
|
+
const inferResult = await managerClient.customTypes.inferSlice({
|
|
325
|
+
source,
|
|
326
|
+
libraryID,
|
|
327
|
+
imageUrl,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
if (currentId !== id.current) return;
|
|
331
|
+
|
|
332
|
+
setSlices((prevSlices) => {
|
|
333
|
+
return prevSlices.map((prevSlice, i) => {
|
|
334
|
+
if (i !== index) return prevSlice;
|
|
335
|
+
return {
|
|
298
336
|
...prevSlice,
|
|
299
|
-
status: "
|
|
337
|
+
status: "success",
|
|
300
338
|
thumbnailUrl: imageUrl,
|
|
301
|
-
|
|
302
|
-
|
|
339
|
+
model: sliceWithoutConflicts({
|
|
340
|
+
existingSlices: existingSlices.current,
|
|
341
|
+
newSlices: slices,
|
|
342
|
+
slice: inferResult.slice,
|
|
343
|
+
}),
|
|
344
|
+
};
|
|
303
345
|
});
|
|
346
|
+
});
|
|
347
|
+
} catch {
|
|
348
|
+
if (currentId !== id.current) return;
|
|
349
|
+
setSlice({
|
|
350
|
+
index,
|
|
351
|
+
slice: (prevSlice) => ({
|
|
352
|
+
...prevSlice,
|
|
353
|
+
status: "generateError",
|
|
354
|
+
thumbnailUrl: imageUrl,
|
|
355
|
+
onRetry: () => {
|
|
356
|
+
void inferSlice({ index, imageUrl, libraryID, source });
|
|
357
|
+
},
|
|
358
|
+
}),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const onAllComplete = async () => {
|
|
364
|
+
const newSlices = slices.reduce<{ upload: NewSlice[]; figma: NewSlice[] }>(
|
|
365
|
+
(acc, slice) => {
|
|
366
|
+
if (slice.status === "success") {
|
|
367
|
+
if (slice.source === "upload") {
|
|
368
|
+
acc.upload.push(slice);
|
|
369
|
+
} else {
|
|
370
|
+
acc.figma.push(slice);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return acc;
|
|
304
374
|
},
|
|
375
|
+
{ upload: [], figma: [] },
|
|
305
376
|
);
|
|
306
|
-
};
|
|
307
377
|
|
|
308
|
-
|
|
309
|
-
const newSlices = slices.reduce<NewSlice[]>((acc, slice) => {
|
|
310
|
-
if (slice.status === "success") acc.push(slice);
|
|
311
|
-
return acc;
|
|
312
|
-
}, []);
|
|
313
|
-
if (!newSlices.length) return;
|
|
378
|
+
if (!newSlices.upload.length && !newSlices.figma.length) return;
|
|
314
379
|
|
|
315
380
|
const currentId = id.current;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
381
|
+
try {
|
|
382
|
+
// Only the slices generated from uploaded images need this step
|
|
383
|
+
const { slices, library } = await addSlices(newSlices.upload);
|
|
384
|
+
if (currentId !== id.current) return;
|
|
385
|
+
|
|
386
|
+
const serverState = await getState();
|
|
387
|
+
createSliceSuccess(serverState.libraries);
|
|
388
|
+
syncChanges();
|
|
389
|
+
|
|
390
|
+
const total = newSlices.upload.length + newSlices.figma.length;
|
|
391
|
+
toast.success(
|
|
392
|
+
`${total} new slice${total > 1 ? "s" : ""} successfully generated.`,
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
onSuccess({ slices: [...newSlices.upload, ...newSlices.figma], library });
|
|
396
|
+
id.current = crypto.randomUUID();
|
|
397
|
+
setSlices([]);
|
|
398
|
+
|
|
399
|
+
void completeStep("createSlice");
|
|
400
|
+
|
|
401
|
+
for (const { model, langSmithUrl } of slices) {
|
|
402
|
+
void telemetry.track({
|
|
403
|
+
event: "slice:created",
|
|
404
|
+
id: model.id,
|
|
405
|
+
name: model.name,
|
|
406
|
+
library,
|
|
407
|
+
location,
|
|
408
|
+
mode: "ai",
|
|
409
|
+
langSmithUrl,
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
addAiFeedback({
|
|
413
|
+
type: "model",
|
|
414
|
+
library,
|
|
415
|
+
sliceId: model.id,
|
|
416
|
+
variationId: model.variations[0].id,
|
|
417
|
+
langSmithUrl,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
} catch {
|
|
421
|
+
if (currentId !== id.current) return;
|
|
422
|
+
toast.error("An unexpected error happened while adding slices.");
|
|
423
|
+
}
|
|
358
424
|
};
|
|
359
425
|
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
426
|
+
const handleClose = () => {
|
|
427
|
+
id.current = crypto.randomUUID();
|
|
428
|
+
setSlices([]);
|
|
429
|
+
onClose();
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const loadingSliceCount = slices.filter((slice) => {
|
|
433
|
+
return slice.status === "uploading" || slice.status === "generating";
|
|
434
|
+
}).length;
|
|
435
|
+
|
|
436
|
+
const pendingSliceCount = slices.filter((slice) => {
|
|
437
|
+
return slice.status === "pending";
|
|
438
|
+
}).length;
|
|
439
|
+
|
|
440
|
+
const hasTriggeredGeneration = slices.some((slice) => {
|
|
441
|
+
return slice.status === "generating" || slice.status === "success";
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
const generateSliceCount = loadingSliceCount + pendingSliceCount;
|
|
365
445
|
|
|
366
446
|
return (
|
|
367
|
-
<Dialog
|
|
368
|
-
|
|
447
|
+
<Dialog
|
|
448
|
+
open={open}
|
|
449
|
+
onOpenChange={loadingSliceCount > 0 ? undefined : onOpenChange}
|
|
450
|
+
>
|
|
451
|
+
<DialogHeader title="Generate with AI" />
|
|
369
452
|
<DialogContent gap={0}>
|
|
370
453
|
<DialogDescription hidden>
|
|
371
454
|
Upload images to generate slices with AI
|
|
372
455
|
</DialogDescription>
|
|
373
456
|
{slices.length === 0 ? (
|
|
374
|
-
<Box
|
|
457
|
+
<Box
|
|
458
|
+
padding={16}
|
|
459
|
+
height="100%"
|
|
460
|
+
gap={16}
|
|
461
|
+
display="flex"
|
|
462
|
+
flexDirection="column"
|
|
463
|
+
>
|
|
464
|
+
<Box
|
|
465
|
+
display="flex"
|
|
466
|
+
gap={16}
|
|
467
|
+
alignItems="center"
|
|
468
|
+
backgroundColor="grey2"
|
|
469
|
+
padding={16}
|
|
470
|
+
borderRadius={12}
|
|
471
|
+
>
|
|
472
|
+
<Box display="flex" gap={8} alignItems="center" flexGrow={1}>
|
|
473
|
+
<Box
|
|
474
|
+
width={48}
|
|
475
|
+
height={48}
|
|
476
|
+
backgroundColor="grey12"
|
|
477
|
+
borderRadius="100%"
|
|
478
|
+
display="flex"
|
|
479
|
+
alignItems="center"
|
|
480
|
+
justifyContent="center"
|
|
481
|
+
>
|
|
482
|
+
<FigmaIcon variant="original" height={25} />
|
|
483
|
+
</Box>
|
|
484
|
+
<Box display="flex" flexDirection="column" flexGrow={1}>
|
|
485
|
+
<Text variant="bold">Want to work faster?</Text>
|
|
486
|
+
<Text variant="small" color="grey11">
|
|
487
|
+
Copy frames from Figma with the Slice Machine plugin and
|
|
488
|
+
paste them here.
|
|
489
|
+
</Text>
|
|
490
|
+
</Box>
|
|
491
|
+
</Box>
|
|
492
|
+
<Button
|
|
493
|
+
endIcon="arrowForward"
|
|
494
|
+
color="indigo"
|
|
495
|
+
onClick={() =>
|
|
496
|
+
window.open(
|
|
497
|
+
"https://www.figma.com/community/plugin/TODO",
|
|
498
|
+
"_blank",
|
|
499
|
+
)
|
|
500
|
+
}
|
|
501
|
+
sx={{ marginRight: 8 }}
|
|
502
|
+
invisible
|
|
503
|
+
>
|
|
504
|
+
Install plugin
|
|
505
|
+
</Button>
|
|
506
|
+
</Box>
|
|
375
507
|
<FileDropZone
|
|
376
508
|
onFilesSelected={onImagesSelected}
|
|
377
509
|
assetType="image"
|
|
@@ -391,28 +523,61 @@ export function CreateSliceFromImageModal(
|
|
|
391
523
|
</FileDropZone>
|
|
392
524
|
</Box>
|
|
393
525
|
) : (
|
|
394
|
-
|
|
526
|
+
<>
|
|
395
527
|
<Box
|
|
396
|
-
display="
|
|
397
|
-
|
|
398
|
-
|
|
528
|
+
display="flex"
|
|
529
|
+
alignItems="center"
|
|
530
|
+
justifyContent="space-between"
|
|
399
531
|
padding={16}
|
|
400
532
|
>
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
533
|
+
<Text variant="h3">Design</Text>
|
|
534
|
+
<FileUploadButton
|
|
535
|
+
size="medium"
|
|
536
|
+
color="grey"
|
|
537
|
+
onFilesSelected={onImagesSelected}
|
|
538
|
+
startIcon="attachFile"
|
|
539
|
+
disabled={hasTriggeredGeneration}
|
|
540
|
+
>
|
|
541
|
+
Add images
|
|
542
|
+
</FileUploadButton>
|
|
404
543
|
</Box>
|
|
405
|
-
|
|
544
|
+
<ScrollArea stableScrollbar={false}>
|
|
545
|
+
<Box
|
|
546
|
+
display="grid"
|
|
547
|
+
gridTemplateColumns="1fr 1fr"
|
|
548
|
+
gap={16}
|
|
549
|
+
padding={16}
|
|
550
|
+
>
|
|
551
|
+
{slices.map((slice, index) => (
|
|
552
|
+
<SliceCard slice={slice} key={`slice-${index}`} />
|
|
553
|
+
))}
|
|
554
|
+
</Box>
|
|
555
|
+
</ScrollArea>
|
|
556
|
+
</>
|
|
406
557
|
)}
|
|
407
558
|
|
|
408
559
|
<DialogActions>
|
|
409
|
-
<DialogCancelButton
|
|
560
|
+
<DialogCancelButton
|
|
561
|
+
size="medium"
|
|
562
|
+
onClick={handleClose}
|
|
563
|
+
disabled={loadingSliceCount > 0}
|
|
564
|
+
>
|
|
565
|
+
Close
|
|
566
|
+
</DialogCancelButton>
|
|
410
567
|
<DialogActionButton
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
onClick={
|
|
568
|
+
color="purple"
|
|
569
|
+
startIcon="autoFixHigh"
|
|
570
|
+
onClick={() => void generateAllPendingSlices()}
|
|
571
|
+
disabled={
|
|
572
|
+
hasTriggeredGeneration ||
|
|
573
|
+
loadingSliceCount > 0 ||
|
|
574
|
+
pendingSliceCount === 0
|
|
575
|
+
}
|
|
576
|
+
loading={hasTriggeredGeneration}
|
|
577
|
+
size="medium"
|
|
414
578
|
>
|
|
415
|
-
{
|
|
579
|
+
Generate {generateSliceCount > 0 ? `(${generateSliceCount}) ` : ""}
|
|
580
|
+
{generateSliceCount === 1 ? "Slice" : "Slices"}
|
|
416
581
|
</DialogActionButton>
|
|
417
582
|
</DialogActions>
|
|
418
583
|
</DialogContent>
|
|
@@ -436,38 +601,49 @@ function UploadBlankSlate(props: {
|
|
|
436
601
|
border
|
|
437
602
|
borderStyle="dashed"
|
|
438
603
|
borderColor={droppingFiles ? "purple9" : "grey6"}
|
|
604
|
+
borderRadius={12}
|
|
605
|
+
flexGrow={1}
|
|
439
606
|
>
|
|
440
607
|
<BlankSlate>
|
|
441
|
-
<
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
startIcon="attachFile"
|
|
454
|
-
onFilesSelected={onFilesSelected}
|
|
455
|
-
color="grey"
|
|
456
|
-
>
|
|
457
|
-
Add images
|
|
458
|
-
</FileUploadButton>
|
|
459
|
-
</BlankSlateActions>
|
|
460
|
-
<BlankSlateDescription>Or</BlankSlateDescription>
|
|
461
|
-
<BlankSlateActions>
|
|
462
|
-
<Button
|
|
463
|
-
size="small"
|
|
464
|
-
startIcon="contentPaste"
|
|
465
|
-
color="grey"
|
|
466
|
-
onClick={onPaste}
|
|
608
|
+
<Box display="flex" flexDirection="column" gap={16} alignItems="center">
|
|
609
|
+
<BlankSlateIcon
|
|
610
|
+
lineColor="purple11"
|
|
611
|
+
backgroundColor="purple5"
|
|
612
|
+
name="cloudUpload"
|
|
613
|
+
size="large"
|
|
614
|
+
/>
|
|
615
|
+
<Box
|
|
616
|
+
display="flex"
|
|
617
|
+
flexDirection="column"
|
|
618
|
+
gap={4}
|
|
619
|
+
alignItems="center"
|
|
467
620
|
>
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
621
|
+
<Text>Generate slices from your designs</Text>
|
|
622
|
+
<Text variant="small" color="grey11">
|
|
623
|
+
Upload your design images or paste them directly from Figma.
|
|
624
|
+
</Text>
|
|
625
|
+
</Box>
|
|
626
|
+
<Box display="flex" alignItems="center" gap={16}>
|
|
627
|
+
<Button
|
|
628
|
+
size="small"
|
|
629
|
+
renderStartIcon={() => (
|
|
630
|
+
<FigmaIcon variant="original" height={16} />
|
|
631
|
+
)}
|
|
632
|
+
color="grey"
|
|
633
|
+
onClick={onPaste}
|
|
634
|
+
>
|
|
635
|
+
Paste from Figma
|
|
636
|
+
</Button>
|
|
637
|
+
<FileUploadButton
|
|
638
|
+
size="small"
|
|
639
|
+
onFilesSelected={onFilesSelected}
|
|
640
|
+
color="purple"
|
|
641
|
+
invisible
|
|
642
|
+
>
|
|
643
|
+
Add images
|
|
644
|
+
</FileUploadButton>
|
|
645
|
+
</Box>
|
|
646
|
+
</Box>
|
|
471
647
|
</BlankSlate>
|
|
472
648
|
</Box>
|
|
473
649
|
);
|
|
@@ -494,6 +670,7 @@ type NewSlice = {
|
|
|
494
670
|
image: File;
|
|
495
671
|
model: SharedSlice;
|
|
496
672
|
langSmithUrl?: string;
|
|
673
|
+
source: "figma" | "upload";
|
|
497
674
|
};
|
|
498
675
|
|
|
499
676
|
/**
|
|
@@ -604,16 +781,3 @@ async function addSlices(newSlices: NewSlice[]) {
|
|
|
604
781
|
|
|
605
782
|
return { library, slices };
|
|
606
783
|
}
|
|
607
|
-
|
|
608
|
-
const getSubmitButtonLabel = (
|
|
609
|
-
location: "custom_type" | "page_type" | "slices",
|
|
610
|
-
) => {
|
|
611
|
-
switch (location) {
|
|
612
|
-
case "custom_type":
|
|
613
|
-
return "Add to type";
|
|
614
|
-
case "page_type":
|
|
615
|
-
return "Add to page";
|
|
616
|
-
case "slices":
|
|
617
|
-
return "Add to slices";
|
|
618
|
-
}
|
|
619
|
-
};
|