@interware/content-generator 0.2.1 → 0.3.0

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.
Files changed (26) hide show
  1. package/README.md +42 -0
  2. package/dist/_chunks/{App-Cbkccs2K.mjs → App-ClgJldBv.mjs} +539 -14
  3. package/dist/_chunks/{App-DCuBRwX8.js → App-CwkLhm0x.js} +538 -13
  4. package/dist/_chunks/{Settings-DuGbtyWC.js → Settings-BEtewXxk.js} +2 -2
  5. package/dist/_chunks/{Settings-B60QGKK4.mjs → Settings-Bc5rcMRs.mjs} +2 -2
  6. package/dist/_chunks/{getTranslation-CacX7ZX5.js → getTranslation-C1jZiBcc.js} +1 -1
  7. package/dist/_chunks/{getTranslation-B97xbmEl.mjs → getTranslation-D3pA_57E.mjs} +1 -1
  8. package/dist/_chunks/{index-BVGbP3E-.js → index-D4lanCw0.js} +2 -2
  9. package/dist/_chunks/{index-B2RuvYul.mjs → index-K1A2opOE.mjs} +2 -2
  10. package/dist/admin/index.js +1 -1
  11. package/dist/admin/index.mjs +1 -1
  12. package/dist/admin/src/components/generate-image/Form.d.ts +15 -0
  13. package/dist/admin/src/components/generate-video/Form.d.ts +36 -0
  14. package/dist/admin/src/containers/generate-image/index.d.ts +2 -0
  15. package/dist/admin/src/containers/generate-video/index.d.ts +2 -0
  16. package/dist/admin/src/lib/use-cases/images/index.d.ts +2 -0
  17. package/dist/admin/src/pages/GenerateImage.d.ts +2 -0
  18. package/dist/admin/src/pages/GenerateVideo.d.ts +2 -0
  19. package/dist/server/index.js +369 -172
  20. package/dist/server/index.mjs +352 -173
  21. package/dist/server/src/controllers/controller.d.ts +2 -0
  22. package/dist/server/src/controllers/index.d.ts +2 -0
  23. package/dist/server/src/index.d.ts +21 -0
  24. package/dist/server/src/services/index.d.ts +19 -0
  25. package/dist/server/src/services/service.d.ts +20 -4
  26. package/package.json +1 -1
package/README.md CHANGED
@@ -1,3 +1,45 @@
1
1
  # strapi-content-generator-gemini
2
2
 
3
3
  Plugin to generate content with Gemini AI
4
+
5
+ # SPEECH
6
+
7
+ # AUDIO PROFILE: IW Robot.
8
+ ## "Te presentamos"
9
+
10
+ ## THE SCENE: Promotional Video
11
+ YouTube, social media, you explain the IW Robot product, its capabilities, functionalities, integrations, benefits, you want to awaken people's interest in the product.
12
+
13
+ ### DIRECTOR'S NOTES
14
+ Style:
15
+ * The "Vocal Smile": The voice conveys a smile and enthusiasm. Its tone is bright, warm, and friendly, inviting the viewer to learn about the product
16
+ * At the end of the speech, use a farewell tone.
17
+ * Don't pronounce IW Robot like an English speaker, but like they would in Mexican Spanish.
18
+
19
+ Rhythm: Energetic and vibrant, with a fast-paced rhythm like the background music. Information is presented quickly, with smooth transitions and strategic pauses between each function to highlight its benefits.
20
+
21
+ Accent: México, Ciudad de México
22
+
23
+ ### SAMPLE CONTEXT
24
+ IW Robot is an RPA that creates a digital workforce that automates manual and semi-automatic tasks, allowing effortless control and operating 24/7.
25
+
26
+ #### TRANSCRIPT
27
+ Es una herramienta avanzada de automatización, monitoreo y observabilidad, que optimiza las operaciones diarias de tu empresa de manera autónoma y adaptable a tus necesidades.
28
+
29
+ IW Robot Monitorea aplicaciones y servicios críticos.
30
+
31
+ Detecta eventos en tiempo real.
32
+
33
+ Notifica de manera instantánea a cualquier aplicación de mensajería.
34
+
35
+ Genera reportes/ con análisis del rendimiento.
36
+
37
+ Se integra con cualquier IA.
38
+
39
+ Personalizable / a tus necesidades.
40
+
41
+ Porque los procesos no pueden esperar. IW Robot te da visibilidad, eficiencia y tranquilidad.
42
+
43
+ IW Robot: Tu caja de herramientas digital a la medida.
44
+
45
+ - Autonoe
@@ -2,12 +2,12 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { useRBAC, useFetchClient, useNotification, Page } from "@strapi/strapi/admin";
3
3
  import { SubNav, SubNavHeader, SubNavSections, SubNavSection, SubNavLink, Box, Table, Thead, Tr, Th, Typography, Flex, Tbody, Td, Button, LinkButton, Dialog, Grid, Field, Textarea, TextInput, Combobox, ComboboxOption, Modal, Card, CardHeader, CardCheckbox, CardAsset, CardBody, CardContent, CardTitle, CardSubtitle, CardBadge, CardAction, IconButton, DesignSystemProvider } from "@strapi/design-system";
4
4
  import { useLocation, useNavigate, Routes, Route } from "react-router-dom";
5
- import { p as pluginPermissions, c as closeDialog, o as openDialog, P as PLUGIN_ID } from "./index-B2RuvYul.mjs";
5
+ import { p as pluginPermissions, c as closeDialog, o as openDialog, P as PLUGIN_ID } from "./index-K1A2opOE.mjs";
6
6
  import { useIntl } from "react-intl";
7
7
  import { useCallback, memo, useMemo, useState, useRef, useEffect, createContext, useContext } from "react";
8
8
  import { useDispatch, useSelector } from "react-redux";
9
- import { Play, ExternalLink, Image as Image$1, Download, Trash } from "@strapi/icons";
10
- import { g as getTranslation } from "./getTranslation-B97xbmEl.mjs";
9
+ import { Play, ExternalLink, Image as Image$1, Download, Trash, PlusCircle } from "@strapi/icons";
10
+ import { g as getTranslation } from "./getTranslation-D3pA_57E.mjs";
11
11
  import { useFormik } from "formik";
12
12
  import * as Yup from "yup";
13
13
  const links = [
@@ -568,24 +568,24 @@ const useUsers = () => {
568
568
  getUsers
569
569
  };
570
570
  };
571
- const validationSchema = Yup.object().shape({
571
+ const validationSchema$2 = Yup.object().shape({
572
572
  titulo: Yup.string().notRequired(),
573
573
  autor: Yup.string().required("Este campo es obligatorio"),
574
574
  categoria: Yup.string().required("Este campo es obligatorio"),
575
575
  temperatura: Yup.string().notRequired(),
576
576
  instrucciones: Yup.string().notRequired()
577
577
  });
578
- const initialValues = {
578
+ const initialValues$1 = {
579
579
  titulo: "",
580
580
  autor: "",
581
581
  categoria: "",
582
- temperatura: "0.2",
582
+ temperatura: "0.5",
583
583
  instrucciones: ""
584
584
  };
585
- const Form = ({ onSubmit, categorias, users }) => {
585
+ const Form$2 = ({ onSubmit, categorias, users }) => {
586
586
  const formik = useFormik({
587
- initialValues,
588
- validationSchema,
587
+ initialValues: initialValues$1,
588
+ validationSchema: validationSchema$2,
589
589
  onSubmit
590
590
  });
591
591
  const { values, errors, touched, isValid, dirty, isSubmitting, handleChange, setFieldValue, handleSubmit } = formik;
@@ -843,7 +843,7 @@ const GenerateContentContainer = () => {
843
843
  /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
844
844
  ] }) }),
845
845
  /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsx(Flex, { alignItems: "end", width: "100%", justifyContent: "end", children: /* @__PURE__ */ jsx(LinkButton, { href: "/admin/plugins/strapi-content-generator-gemini", children: "Regresar" }) }) }),
846
- /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsx(Form, { categorias, users, onSubmit }) })
846
+ /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsx(Form$2, { categorias, users, onSubmit }) })
847
847
  ] })
848
848
  ] });
849
849
  };
@@ -854,7 +854,7 @@ const GenerateContent = () => {
854
854
  ] });
855
855
  };
856
856
  const useImages$1 = () => {
857
- const { get, del } = useFetchClient();
857
+ const { get, del, post } = useFetchClient();
858
858
  const getImages = useCallback(
859
859
  async () => {
860
860
  return await get("/strapi-content-generator-gemini/images");
@@ -867,9 +867,23 @@ const useImages$1 = () => {
867
867
  },
868
868
  [del]
869
869
  );
870
+ const generateImageWithConfig = useCallback(
871
+ async (data, config) => {
872
+ return await post("/strapi-content-generator-gemini/generate-image-with-config", data, config);
873
+ },
874
+ [post]
875
+ );
876
+ const generateVideoWithScenes = useCallback(
877
+ async (data, config) => {
878
+ return await post("/strapi-content-generator-gemini/generate-video-with-scenes", data, config);
879
+ },
880
+ [post]
881
+ );
870
882
  return {
871
883
  getImages,
872
- deleteImage
884
+ deleteImage,
885
+ generateImageWithConfig,
886
+ generateVideoWithScenes
873
887
  };
874
888
  };
875
889
  const PRINCIPAL_KEY = "principal";
@@ -1172,9 +1186,10 @@ const Images = () => {
1172
1186
  const GenerateImagesContainer = () => {
1173
1187
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ImagesProvider, { children: /* @__PURE__ */ jsxs(Grid.Root, { padding: 6, children: [
1174
1188
  /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", children: [
1175
- /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "Generar imagenes con IA" }),
1189
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "Generar imagenes y videos con IA" }),
1176
1190
  /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
1177
1191
  ] }) }),
1192
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "end", alignItems: "end", gap: 3, width: "100%", children: /* @__PURE__ */ jsx(LinkButton, { href: "/admin/plugins/strapi-content-generator-gemini/generate-image", children: "Generar nueva imagen" }) }) }),
1178
1193
  /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsx(Images, {}) })
1179
1194
  ] }) }) });
1180
1195
  };
@@ -1184,6 +1199,512 @@ const GenerateImages = () => {
1184
1199
  /* @__PURE__ */ jsx(GenerateImagesContainer, {})
1185
1200
  ] });
1186
1201
  };
1202
+ const aspectRatioOptions = [
1203
+ { value: "1:1", label: "1:1 (Cuadrado)" },
1204
+ { value: "2:3", label: "2:3 Rectangular (vertical)" },
1205
+ { value: "3:2", label: "3:2 Rectangular (horizontal)" },
1206
+ { value: "3:4", label: "3:4 Rectangular (vertical)" },
1207
+ { value: "4:3", label: "4:3 Rectangular (horizontal)" },
1208
+ { value: "9:16", label: "9:16 Rectangular (vertical)" },
1209
+ { value: "16:9", label: "16:9 Rectangular (horizontal)" },
1210
+ { value: "21:9", label: "21:9 Rectangular (horizontal)" }
1211
+ ];
1212
+ const validationSchema$1 = Yup.object({
1213
+ prompt: Yup.string().required("El prompt es requerido"),
1214
+ aspectRatio: Yup.string().required("La relación de aspecto es requerida"),
1215
+ imageSize: Yup.string().required("El tamaño de la imagen es requerido")
1216
+ });
1217
+ const Form$1 = ({ onSubmit }) => {
1218
+ const [referenceImages, setReferenceImages] = useState([]);
1219
+ const individualFileInputRefs = useRef({});
1220
+ const newImageFileInputRef = useRef(null);
1221
+ const formik = useFormik({
1222
+ initialValues: {
1223
+ prompt: "",
1224
+ aspectRatio: "16:9",
1225
+ imageSize: "1K"
1226
+ },
1227
+ validationSchema: validationSchema$1,
1228
+ onSubmit: (values, formikHelpers) => onSubmit({ ...values }, formikHelpers, referenceImages)
1229
+ });
1230
+ const handleAddImage = () => {
1231
+ newImageFileInputRef.current?.click();
1232
+ };
1233
+ const handleRemoveImage = (id) => {
1234
+ const imageToRemove = referenceImages.find((img) => img.id === id);
1235
+ if (imageToRemove?.previewUrl) {
1236
+ URL.revokeObjectURL(imageToRemove.previewUrl);
1237
+ }
1238
+ setReferenceImages(referenceImages.filter((image) => image.id !== id));
1239
+ };
1240
+ const handleNewImageFileChange = (e) => {
1241
+ const selectedFile = e.target.files?.[0];
1242
+ if (selectedFile) {
1243
+ setReferenceImages((prevImages) => [
1244
+ ...prevImages,
1245
+ { id: Date.now(), file: selectedFile, previewUrl: URL.createObjectURL(selectedFile) }
1246
+ ]);
1247
+ }
1248
+ e.target.value = "";
1249
+ };
1250
+ const handleIndividualFileChange = (id, selectedFile) => {
1251
+ setReferenceImages((prevImages) => prevImages.map((image) => {
1252
+ if (image.id === id) {
1253
+ if (image.previewUrl) {
1254
+ URL.revokeObjectURL(image.previewUrl);
1255
+ }
1256
+ if (selectedFile) {
1257
+ return { ...image, file: selectedFile, previewUrl: URL.createObjectURL(selectedFile) };
1258
+ } else {
1259
+ const { file, previewUrl, ...rest } = image;
1260
+ return rest;
1261
+ }
1262
+ }
1263
+ return image;
1264
+ }));
1265
+ };
1266
+ return /* @__PURE__ */ jsx(Box, { background: "neutral0", padding: 6, hasRadius: true, width: "100%", children: /* @__PURE__ */ jsx("form", { onSubmit: formik.handleSubmit, children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, children: [
1267
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsxs(
1268
+ Field.Root,
1269
+ {
1270
+ id: "prompt",
1271
+ width: "100%",
1272
+ required: true,
1273
+ children: [
1274
+ /* @__PURE__ */ jsx(Field.Label, { children: "Prompt de la imagen" }),
1275
+ /* @__PURE__ */ jsx(
1276
+ Textarea,
1277
+ {
1278
+ name: "prompt",
1279
+ placeholder: "Ej: Un paisaje futurista de la Ciudad de México con neones.",
1280
+ onChange: formik.handleChange,
1281
+ onBlur: formik.handleBlur,
1282
+ value: formik.values.prompt,
1283
+ rows: 12
1284
+ }
1285
+ )
1286
+ ]
1287
+ }
1288
+ ) }),
1289
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsxs(
1290
+ Field.Root,
1291
+ {
1292
+ id: "imagen-referencia",
1293
+ width: "100%",
1294
+ children: [
1295
+ /* @__PURE__ */ jsx(Field.Label, { children: "Imagen(es) de referencia" }),
1296
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, width: "100%", children: [
1297
+ referenceImages.map((image) => /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 3, hasRadius: true, width: "100%", children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 3, width: "100%", children: [
1298
+ /* @__PURE__ */ jsx(Box, { background: "primary100", padding: 1, children: image.previewUrl ? /* @__PURE__ */ jsx("img", { src: image.previewUrl, alt: "preview", style: { width: "50px", height: "50px", objectFit: "cover" } }) : /* @__PURE__ */ jsx("div", { style: { width: "50px", height: "50px", backgroundColor: "#ccc" } }) }),
1299
+ /* @__PURE__ */ jsxs("div", { style: { flexGrow: 2 }, children: [
1300
+ /* @__PURE__ */ jsx(
1301
+ "input",
1302
+ {
1303
+ type: "file",
1304
+ accept: "image/*",
1305
+ ref: (el) => individualFileInputRefs.current[image.id] = el,
1306
+ style: { display: "none" },
1307
+ onChange: (e) => handleIndividualFileChange(image.id, e.target.files?.[0])
1308
+ }
1309
+ ),
1310
+ /* @__PURE__ */ jsx(
1311
+ Button,
1312
+ {
1313
+ onClick: () => individualFileInputRefs.current[image.id]?.click(),
1314
+ startIcon: image.file ? void 0 : /* @__PURE__ */ jsx(PlusCircle, {}),
1315
+ variant: "secondary",
1316
+ fullWidth: true,
1317
+ children: image.file ? image.file.name : "Seleccionar archivo"
1318
+ }
1319
+ )
1320
+ ] }),
1321
+ /* @__PURE__ */ jsx(IconButton, { onClick: () => handleRemoveImage(image.id), label: "Eliminar imagen", children: /* @__PURE__ */ jsx(Trash, {}) })
1322
+ ] }) }, image.id)),
1323
+ /* @__PURE__ */ jsx(
1324
+ "input",
1325
+ {
1326
+ type: "file",
1327
+ accept: "image/*",
1328
+ ref: newImageFileInputRef,
1329
+ style: { display: "none" },
1330
+ onChange: handleNewImageFileChange
1331
+ }
1332
+ ),
1333
+ /* @__PURE__ */ jsx(
1334
+ Button,
1335
+ {
1336
+ startIcon: /* @__PURE__ */ jsx(PlusCircle, {}),
1337
+ variant: "secondary",
1338
+ fullWidth: true,
1339
+ onClick: handleAddImage,
1340
+ disabled: referenceImages.length >= 5,
1341
+ children: "Añadir imagen de referencia"
1342
+ }
1343
+ )
1344
+ ] }),
1345
+ /* @__PURE__ */ jsx(Field.Error, {}),
1346
+ /* @__PURE__ */ jsx(Field.Hint, {})
1347
+ ]
1348
+ }
1349
+ ) }),
1350
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsxs(
1351
+ Field.Root,
1352
+ {
1353
+ id: "aspectRatio",
1354
+ width: "100%",
1355
+ required: true,
1356
+ children: [
1357
+ /* @__PURE__ */ jsx(Field.Label, { children: "Relación de aspecto" }),
1358
+ /* @__PURE__ */ jsx(
1359
+ Combobox,
1360
+ {
1361
+ name: "aspectRatio",
1362
+ onChange: (value) => formik.setFieldValue("aspectRatio", value),
1363
+ onBlur: () => formik.setFieldTouched("aspectRatio", true),
1364
+ value: formik.values.aspectRatio,
1365
+ onClear: () => formik.setFieldValue("aspectRatio", ""),
1366
+ children: aspectRatioOptions.map((option) => /* @__PURE__ */ jsx(ComboboxOption, { value: option.value, children: option.label }, option.value))
1367
+ }
1368
+ )
1369
+ ]
1370
+ }
1371
+ ) }),
1372
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsxs(
1373
+ Field.Root,
1374
+ {
1375
+ id: "imageSize",
1376
+ error: formik.touched.imageSize && formik.errors.imageSize ? formik.errors.imageSize : void 0,
1377
+ width: "100%",
1378
+ required: true,
1379
+ children: [
1380
+ /* @__PURE__ */ jsx(Field.Label, { children: "Tamaño de la imagen" }),
1381
+ /* @__PURE__ */ jsxs(
1382
+ Combobox,
1383
+ {
1384
+ name: "imageSize",
1385
+ onChange: (value) => formik.setFieldValue("imageSize", value),
1386
+ onBlur: () => formik.setFieldTouched("imageSize", true),
1387
+ value: formik.values.imageSize,
1388
+ onClear: () => formik.setFieldValue("imageSize", ""),
1389
+ children: [
1390
+ /* @__PURE__ */ jsx(ComboboxOption, { value: "1K", children: "1K" }),
1391
+ /* @__PURE__ */ jsx(ComboboxOption, { value: "2K", children: "2K" }),
1392
+ /* @__PURE__ */ jsx(ComboboxOption, { value: "4K", children: "4K" })
1393
+ ]
1394
+ }
1395
+ ),
1396
+ /* @__PURE__ */ jsx(Field.Error, { children: formik.errors.imageSize })
1397
+ ]
1398
+ }
1399
+ ) }),
1400
+ /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(Flex, { width: "100%", justifyContent: "end", alignItems: "end", children: /* @__PURE__ */ jsx(
1401
+ Button,
1402
+ {
1403
+ type: "submit",
1404
+ fullWidth: true,
1405
+ disabled: !(formik.isValid && formik.dirty) || formik.isSubmitting,
1406
+ loading: formik.isSubmitting,
1407
+ children: "Generar imagen"
1408
+ }
1409
+ ) }) })
1410
+ ] }) }) });
1411
+ };
1412
+ const GenerateImageContainer = () => {
1413
+ const { toggleNotification } = useNotification();
1414
+ const { generateImageWithConfig } = useImages$1();
1415
+ const { formatMessage } = useIntl();
1416
+ const handleSubmit = async (values, { setSubmitting }, referenceImages) => {
1417
+ try {
1418
+ const formData = new FormData();
1419
+ formData.append("prompt", values.prompt);
1420
+ formData.append("aspectRatio", values.aspectRatio);
1421
+ formData.append("imageSize", values.imageSize);
1422
+ referenceImages.forEach((img, index) => {
1423
+ if (img.file) {
1424
+ formData.append(`referenceImages[${index}]`, img.file);
1425
+ }
1426
+ });
1427
+ const response = await generateImageWithConfig(formData);
1428
+ toggleNotification({
1429
+ type: "success",
1430
+ message: formatMessage({
1431
+ id: `${PLUGIN_ID}.config.load.success`,
1432
+ defaultMessage: "Imagen generada con éxito"
1433
+ })
1434
+ });
1435
+ } catch (error) {
1436
+ toggleNotification({
1437
+ type: "danger",
1438
+ message: formatMessage({
1439
+ id: `${PLUGIN_ID}.config.load.error`,
1440
+ defaultMessage: "Ocurrió un error al generar la imagen"
1441
+ })
1442
+ });
1443
+ } finally {
1444
+ setSubmitting(false);
1445
+ }
1446
+ };
1447
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1448
+ /* @__PURE__ */ jsx(Grid.Root, { padding: 6, children: /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", children: [
1449
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "Generar imagen con IA" }),
1450
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
1451
+ ] }) }) }),
1452
+ /* @__PURE__ */ jsxs(Grid.Root, { padding: 6, children: [
1453
+ /* @__PURE__ */ jsx(Grid.Item, { m: 2 }),
1454
+ /* @__PURE__ */ jsx(Grid.Item, { m: 8, children: /* @__PURE__ */ jsx(Form$1, { onSubmit: handleSubmit }) })
1455
+ ] })
1456
+ ] });
1457
+ };
1458
+ const GenerateImage = () => {
1459
+ return /* @__PURE__ */ jsxs(Page.Main, { children: [
1460
+ /* @__PURE__ */ jsx(Page.Title, { children: "Generar imagen con IA" }),
1461
+ /* @__PURE__ */ jsx(GenerateImageContainer, {})
1462
+ ] });
1463
+ };
1464
+ const sceneSchema = Yup.object().shape({
1465
+ prompt: Yup.string().trim().required("El prompt es obligatorio para esta escena"),
1466
+ durationSeconds: Yup.number().min(1, "La duración mínima es 1 segundo").max(90, "La duración máxima es 90 segundos").required("La duración es obligatoria"),
1467
+ referenceImages: Yup.array().of(
1468
+ Yup.object().shape({
1469
+ id: Yup.number().required(),
1470
+ file: Yup.mixed().optional(),
1471
+ previewUrl: Yup.string().optional()
1472
+ })
1473
+ ).max(3, "Se permiten máximo 3 imágenes de referencia por escena").optional()
1474
+ });
1475
+ const validationSchema = Yup.object().shape({
1476
+ aspectRatio: Yup.string().oneOf(["16:9", "9:16", "1:1"], "Aspect ratio no válido").required("El aspect ratio es obligatorio"),
1477
+ scenes: Yup.array().of(sceneSchema).min(1, "Debe haber al menos una escena").max(20, "No se permiten más de 20 escenas")
1478
+ });
1479
+ const initialValues = {
1480
+ aspectRatio: "16:9",
1481
+ scenes: [
1482
+ {
1483
+ prompt: ``,
1484
+ durationSeconds: 8,
1485
+ narrationText: ``,
1486
+ referenceImages: []
1487
+ }
1488
+ ]
1489
+ };
1490
+ const Form = ({ onSubmit }) => {
1491
+ const formik = useFormik({
1492
+ initialValues,
1493
+ validationSchema,
1494
+ validateOnMount: true,
1495
+ onSubmit
1496
+ });
1497
+ console.log("🚀 ~ Form ~ formik:", formik.errors);
1498
+ return /* @__PURE__ */ jsx(Box, { background: "neutral0", padding: 6, hasRadius: true, width: "100%", children: /* @__PURE__ */ jsx("form", { onSubmit: formik.handleSubmit, children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, children: [
1499
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsxs(Field.Root, { id: "aspectRatio", width: "100%", required: true, children: [
1500
+ /* @__PURE__ */ jsx(Field.Label, { children: "Relación de aspecto" }),
1501
+ /* @__PURE__ */ jsxs(
1502
+ Combobox,
1503
+ {
1504
+ name: "aspectRatio",
1505
+ onChange: (value) => formik.setFieldValue("aspectRatio", value),
1506
+ onBlur: () => formik.setFieldTouched("aspectRatio", true),
1507
+ value: formik.values.aspectRatio,
1508
+ onClear: () => formik.setFieldValue("aspectRatio", ""),
1509
+ children: [
1510
+ /* @__PURE__ */ jsx(ComboboxOption, { value: "16:9", children: "16:9" }),
1511
+ /* @__PURE__ */ jsx(ComboboxOption, { value: "9:16", children: "9:16" }),
1512
+ /* @__PURE__ */ jsx(ComboboxOption, { value: "1:1", children: "1:1" })
1513
+ ]
1514
+ }
1515
+ ),
1516
+ /* @__PURE__ */ jsx(Field.Error, { children: formik.errors.aspectRatio })
1517
+ ] }) }),
1518
+ formik.values.scenes.map((scene, index) => /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Box, { background: "neutral100", padding: 4, hasRadius: true, width: "100%", children: [
1519
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
1520
+ /* @__PURE__ */ jsxs("strong", { children: [
1521
+ "Escena ",
1522
+ index + 1
1523
+ ] }),
1524
+ formik.values.scenes.length > 1 && /* @__PURE__ */ jsx(
1525
+ Button,
1526
+ {
1527
+ variant: "danger-light",
1528
+ onClick: () => {
1529
+ const updatedScenes = [...formik.values.scenes];
1530
+ updatedScenes.splice(index, 1);
1531
+ formik.setFieldValue("scenes", updatedScenes);
1532
+ },
1533
+ children: "Eliminar Escena"
1534
+ }
1535
+ )
1536
+ ] }),
1537
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
1538
+ /* @__PURE__ */ jsxs(Field.Root, { id: `scenes.${index}.prompt`, width: "100%", required: true, children: [
1539
+ /* @__PURE__ */ jsx(Field.Label, { children: "Prompt de la escena" }),
1540
+ /* @__PURE__ */ jsx(
1541
+ Textarea,
1542
+ {
1543
+ name: `scenes.${index}.prompt`,
1544
+ placeholder: "Describe visualmente esta escena",
1545
+ onChange: formik.handleChange,
1546
+ onBlur: formik.handleBlur,
1547
+ value: formik.values.scenes[index].prompt,
1548
+ rows: 6
1549
+ }
1550
+ )
1551
+ ] }),
1552
+ /* @__PURE__ */ jsxs(Field.Root, { id: `scenes.${index}.durationSeconds`, width: "100%", required: true, children: [
1553
+ /* @__PURE__ */ jsx(Field.Label, { children: "Duración (segundos)" }),
1554
+ /* @__PURE__ */ jsxs(
1555
+ Combobox,
1556
+ {
1557
+ name: `scenes.${index}.durationSeconds`,
1558
+ onChange: (value) => formik.setFieldValue(`scenes.${index}.durationSeconds`, value),
1559
+ onBlur: formik.handleBlur,
1560
+ value: formik.values.scenes[index].durationSeconds,
1561
+ onClear: () => formik.setFieldValue(`scenes.${index}.durationSeconds`, ""),
1562
+ children: [
1563
+ /* @__PURE__ */ jsx(ComboboxOption, { value: 4, children: "4" }),
1564
+ /* @__PURE__ */ jsx(ComboboxOption, { value: 6, children: "6" }),
1565
+ /* @__PURE__ */ jsx(ComboboxOption, { value: 8, children: "8" })
1566
+ ]
1567
+ }
1568
+ )
1569
+ ] }),
1570
+ /* @__PURE__ */ jsxs(Field.Root, { id: `scenes.${index}.referenceImages`, width: "100%", children: [
1571
+ /* @__PURE__ */ jsx(Field.Label, { children: "Imagen(es) de referencia" }),
1572
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, width: "100%", children: [
1573
+ scene.referenceImages.map((image, imgIndex) => /* @__PURE__ */ jsx(Box, { background: "neutral200", padding: 2, hasRadius: true, width: "100%", children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
1574
+ image.previewUrl ? /* @__PURE__ */ jsx("img", { src: image.previewUrl, alt: "preview", style: { width: 50, height: 50, objectFit: "cover" } }) : /* @__PURE__ */ jsx("div", { style: { width: 50, height: 50, backgroundColor: "#ccc" } }),
1575
+ /* @__PURE__ */ jsx(
1576
+ IconButton,
1577
+ {
1578
+ onClick: () => {
1579
+ const imageToRemove = scene.referenceImages[imgIndex];
1580
+ if (imageToRemove.previewUrl) {
1581
+ URL.revokeObjectURL(imageToRemove.previewUrl);
1582
+ }
1583
+ const updatedImages = [...scene.referenceImages];
1584
+ updatedImages.splice(imgIndex, 1);
1585
+ formik.setFieldValue(`scenes.${index}.referenceImages`, updatedImages);
1586
+ },
1587
+ label: "Eliminar imagen",
1588
+ children: /* @__PURE__ */ jsx(Trash, {})
1589
+ }
1590
+ )
1591
+ ] }) }, image.id)),
1592
+ /* @__PURE__ */ jsx(
1593
+ Button,
1594
+ {
1595
+ type: "button",
1596
+ startIcon: /* @__PURE__ */ jsx(PlusCircle, {}),
1597
+ variant: "secondary",
1598
+ fullWidth: true,
1599
+ onClick: () => {
1600
+ const input = document.createElement("input");
1601
+ input.type = "file";
1602
+ input.accept = "image/*";
1603
+ input.onchange = (e) => {
1604
+ const selectedFile = e.target.files?.[0];
1605
+ if (!selectedFile) return;
1606
+ const newImage = {
1607
+ id: Date.now(),
1608
+ file: selectedFile,
1609
+ previewUrl: URL.createObjectURL(selectedFile)
1610
+ };
1611
+ const newImages = [...scene.referenceImages, newImage];
1612
+ formik.setFieldValue(`scenes.${index}.referenceImages`, newImages.slice(0, 5));
1613
+ };
1614
+ input.click();
1615
+ },
1616
+ disabled: scene.referenceImages.length >= 5,
1617
+ children: "Añadir imagen de referencia"
1618
+ }
1619
+ )
1620
+ ] })
1621
+ ] })
1622
+ ] })
1623
+ ] }, index) }, `scene-${index}`)),
1624
+ /* @__PURE__ */ jsx(Grid.Item, { m: 12, children: /* @__PURE__ */ jsx(
1625
+ Button,
1626
+ {
1627
+ type: "button",
1628
+ startIcon: /* @__PURE__ */ jsx(PlusCircle, {}),
1629
+ variant: "secondary",
1630
+ onClick: () => {
1631
+ formik.setFieldValue("scenes", [
1632
+ ...formik.values.scenes,
1633
+ { prompt: "", durationSeconds: 6, narrationText: "", referenceImages: [] }
1634
+ ]);
1635
+ },
1636
+ fullWidth: true,
1637
+ children: "Añadir Escena"
1638
+ }
1639
+ ) }),
1640
+ /* @__PURE__ */ jsx(Grid.Item, { col: 12, children: /* @__PURE__ */ jsx(Flex, { width: "100%", justifyContent: "end", alignItems: "end", children: /* @__PURE__ */ jsx(
1641
+ Button,
1642
+ {
1643
+ type: "submit",
1644
+ fullWidth: true,
1645
+ disabled: !(formik.isValid && formik.dirty) || formik.isSubmitting,
1646
+ loading: formik.isSubmitting,
1647
+ children: "Generar Video"
1648
+ }
1649
+ ) }) })
1650
+ ] }) }) });
1651
+ };
1652
+ const GenerateVideoContainer = () => {
1653
+ const { toggleNotification } = useNotification();
1654
+ const { generateVideoWithScenes } = useImages$1();
1655
+ const { formatMessage } = useIntl();
1656
+ const handleSubmit = async (values, { setSubmitting }) => {
1657
+ try {
1658
+ const formData = new FormData();
1659
+ formData.append("aspectRatio", values.aspectRatio);
1660
+ values.scenes.forEach((scene, index) => {
1661
+ formData.append(`scenes[${index}][prompt]`, scene.prompt);
1662
+ formData.append(`scenes[${index}][durationSeconds]`, scene.durationSeconds.toString());
1663
+ formData.append(`scenes[${index}][narrationText]`, scene.narrationText || "");
1664
+ scene.referenceImages.forEach((image, imgIndex) => {
1665
+ if (image.file) {
1666
+ formData.append(`scenes[${index}][referenceImages][${imgIndex}]`, image.file);
1667
+ }
1668
+ });
1669
+ });
1670
+ const response = await generateVideoWithScenes(formData);
1671
+ console.log("🚀 ~ handleSubmit ~ response:", response);
1672
+ toggleNotification({
1673
+ type: "success",
1674
+ message: formatMessage({
1675
+ id: `${PLUGIN_ID}.config.load.success`,
1676
+ defaultMessage: "Video generado con éxito"
1677
+ })
1678
+ });
1679
+ } catch (error) {
1680
+ toggleNotification({
1681
+ type: "danger",
1682
+ message: formatMessage({
1683
+ id: `${PLUGIN_ID}.config.load.error`,
1684
+ defaultMessage: "Ocurrió un error al generar el video"
1685
+ })
1686
+ });
1687
+ } finally {
1688
+ setSubmitting(false);
1689
+ }
1690
+ };
1691
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1692
+ /* @__PURE__ */ jsx(Grid.Root, { padding: 6, children: /* @__PURE__ */ jsx(Grid.Item, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "start", children: [
1693
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "Generar video con IA" }),
1694
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", children: "Para uso exclusivo de InterWare de México" })
1695
+ ] }) }) }),
1696
+ /* @__PURE__ */ jsxs(Grid.Root, { padding: 6, children: [
1697
+ /* @__PURE__ */ jsx(Grid.Item, { m: 2 }),
1698
+ /* @__PURE__ */ jsx(Grid.Item, { m: 8, children: /* @__PURE__ */ jsx(Form, { onSubmit: handleSubmit }) })
1699
+ ] })
1700
+ ] });
1701
+ };
1702
+ const GenerateVideo = () => {
1703
+ return /* @__PURE__ */ jsxs(Page.Main, { children: [
1704
+ /* @__PURE__ */ jsx(Page.Title, { children: "Generar video con IA" }),
1705
+ /* @__PURE__ */ jsx(GenerateVideoContainer, {})
1706
+ ] });
1707
+ };
1187
1708
  const App = () => {
1188
1709
  const { allowedActions, isLoading } = useRBAC(pluginPermissions);
1189
1710
  if (isLoading) {
@@ -1203,7 +1724,11 @@ const App = () => {
1203
1724
  /* @__PURE__ */ jsxs(Routes, { children: [
1204
1725
  allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }),
1205
1726
  allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { path: "/generate-content", element: /* @__PURE__ */ jsx(GenerateContent, {}) }),
1206
- allowedActions.canAccess && /* @__PURE__ */ jsx(Route, { path: "/generate-images", element: /* @__PURE__ */ jsx(GenerateImages, {}) }),
1727
+ allowedActions.canAccess && /* @__PURE__ */ jsxs(Fragment, { children: [
1728
+ /* @__PURE__ */ jsx(Route, { path: "/generate-images", element: /* @__PURE__ */ jsx(GenerateImages, {}) }),
1729
+ /* @__PURE__ */ jsx(Route, { path: "/generate-image", element: /* @__PURE__ */ jsx(GenerateImage, {}) }),
1730
+ /* @__PURE__ */ jsx(Route, { path: "/generate-video", element: /* @__PURE__ */ jsx(GenerateVideo, {}) })
1731
+ ] }),
1207
1732
  /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) })
1208
1733
  ] })
1209
1734
  ] })