@interware/content-generator 0.7.2 → 0.8.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.
- package/dist/_chunks/{App-CTh6owSM.mjs → App-DD1MzaWm.mjs} +2 -2
- package/dist/_chunks/{App-ugsCmlsC.js → App-DspaMfyW.js} +2 -2
- package/dist/_chunks/{ImageStudioTabs-Umf3GODW.mjs → ImageStudioTabs-DwaLxRjl.mjs} +308 -16
- package/dist/_chunks/{ImageStudioTabs-BFEF9ggK.js → ImageStudioTabs-uXSBkFeu.js} +306 -14
- package/dist/_chunks/{Settings-CdGW2O8h.mjs → Settings-C6TbUrgU.mjs} +170 -2
- package/dist/_chunks/{Settings-Sk78JUBS.js → Settings-ps0o7-fC.js} +169 -1
- package/dist/_chunks/{StudioApp-CrGn-9Gg.mjs → StudioApp-B5I1OfGw.mjs} +2 -2
- package/dist/_chunks/{StudioApp-DoC0z4eh.js → StudioApp-B7kGpqqo.js} +2 -2
- package/dist/_chunks/{index-C2s9Jhcc.js → index-BudahdfX.js} +3 -3
- package/dist/_chunks/{index-D6UAvh53.mjs → index-zenHkX-p.mjs} +3 -3
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/settings/DriveSettings.d.ts +77 -0
- package/dist/admin/src/features/drive/DriveTab.d.ts +42 -0
- package/dist/admin/src/features/image-studio/ImageStudioTabs.d.ts +3 -0
- package/dist/admin/src/features/library/FileCard.d.ts +5 -2
- package/dist/admin/src/features/library/driveSync.d.ts +41 -0
- package/dist/admin/src/features/templates/export/templateOrigin.d.ts +10 -0
- package/dist/admin/src/lib/use-cases/drive/index.d.ts +3 -0
- package/dist/server/index.js +1411 -49
- package/dist/server/index.mjs +1409 -47
- package/dist/server/src/bootstrap.d.ts +2 -0
- package/dist/server/src/content-types/drive-link/index.d.ts +62 -0
- package/dist/server/src/content-types/drive-link/schema.d.ts +68 -0
- package/dist/server/src/content-types/drive-op/index.d.ts +46 -0
- package/dist/server/src/content-types/drive-op/schema.d.ts +49 -0
- package/dist/server/src/content-types/index.d.ts +106 -0
- package/dist/server/src/controllers/drive.d.ts +30 -0
- package/dist/server/src/controllers/index.d.ts +130 -0
- package/dist/server/src/drive/client.d.ts +32 -0
- package/dist/server/src/drive/clientConfig.d.ts +27 -0
- package/dist/server/src/drive/common.d.ts +6 -0
- package/dist/server/src/drive/fakes.d.ts +104 -0
- package/dist/server/src/drive/files.d.ts +136 -0
- package/dist/server/src/drive/lifecycles.d.ts +7 -0
- package/dist/server/src/drive/links.d.ts +23 -0
- package/dist/server/src/drive/model.d.ts +99 -0
- package/dist/server/src/drive/oauth.d.ts +47 -0
- package/dist/server/src/drive/pull.d.ts +23 -0
- package/dist/server/src/drive/strapiPorts.d.ts +10 -0
- package/dist/server/src/drive/sync.d.ts +40 -0
- package/dist/server/src/index.d.ts +294 -0
- package/dist/server/src/services/drive.d.ts +141 -0
- package/dist/server/src/services/index.d.ts +55 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const jsxRuntime = require("react/jsx-runtime");
|
|
3
3
|
const designSystem = require("@strapi/design-system");
|
|
4
4
|
const reactRouterDom = require("react-router-dom");
|
|
5
|
-
const index = require("./index-
|
|
5
|
+
const index = require("./index-BudahdfX.js");
|
|
6
6
|
const react = require("react");
|
|
7
7
|
const icons = require("@strapi/icons");
|
|
8
8
|
const styledComponents = require("styled-components");
|
|
@@ -1174,7 +1174,7 @@ const closestRatio = (width, height, ratios) => {
|
|
|
1174
1174
|
};
|
|
1175
1175
|
return ratios.reduce((best, r) => Math.abs(value(r) - target) < Math.abs(value(best) - target) ? r : best);
|
|
1176
1176
|
};
|
|
1177
|
-
const BASE = "/strapi-content-generator-gemini/image-studio";
|
|
1177
|
+
const BASE$2 = "/strapi-content-generator-gemini/image-studio";
|
|
1178
1178
|
const toResult = ({ image, mimeType, model, grounding }) => ({
|
|
1179
1179
|
src: `data:${mimeType};base64,${image}`,
|
|
1180
1180
|
model,
|
|
@@ -1186,7 +1186,7 @@ const errorMessage = (err, fallback) => {
|
|
|
1186
1186
|
};
|
|
1187
1187
|
const createGeneratorApi = (client) => ({
|
|
1188
1188
|
async models() {
|
|
1189
|
-
const { data } = await client.get(`${BASE}/models`);
|
|
1189
|
+
const { data } = await client.get(`${BASE$2}/models`);
|
|
1190
1190
|
return data;
|
|
1191
1191
|
},
|
|
1192
1192
|
async generate(p) {
|
|
@@ -1199,7 +1199,7 @@ const createGeneratorApi = (client) => ({
|
|
|
1199
1199
|
form.append("allowPeople", String(p.allowPeople));
|
|
1200
1200
|
form.append("grounding", String(p.grounding));
|
|
1201
1201
|
p.references.forEach((f) => form.append("references", f, f.name));
|
|
1202
|
-
const { data } = await client.post(`${BASE}/generate`, form);
|
|
1202
|
+
const { data } = await client.post(`${BASE$2}/generate`, form);
|
|
1203
1203
|
return toResult(data);
|
|
1204
1204
|
},
|
|
1205
1205
|
async edit(p) {
|
|
@@ -1212,11 +1212,11 @@ const createGeneratorApi = (client) => ({
|
|
|
1212
1212
|
p.references.forEach((f) => form.append("references", f, f.name));
|
|
1213
1213
|
if (p.aspectRatio) form.append("aspectRatio", p.aspectRatio);
|
|
1214
1214
|
form.append("image", new File([blob], `base.${blob.type.split("/")[1] ?? "png"}`, { type: blob.type }));
|
|
1215
|
-
const { data } = await client.post(`${BASE}/edit`, form);
|
|
1215
|
+
const { data } = await client.post(`${BASE$2}/edit`, form);
|
|
1216
1216
|
return toResult(data);
|
|
1217
1217
|
},
|
|
1218
1218
|
async expand(prompt) {
|
|
1219
|
-
const { data } = await client.post(`${BASE}/expand`, { prompt });
|
|
1219
|
+
const { data } = await client.post(`${BASE$2}/expand`, { prompt });
|
|
1220
1220
|
return data.prompt;
|
|
1221
1221
|
}
|
|
1222
1222
|
});
|
|
@@ -1838,7 +1838,7 @@ const isImage = (f) => (f.mime ?? "").startsWith("image/");
|
|
|
1838
1838
|
const canRemoveBackground = (f) => isImage(f) && f.mime !== "image/svg+xml";
|
|
1839
1839
|
const isGenerated = (f) => (f.caption ?? "").startsWith("AI generated content");
|
|
1840
1840
|
const formatSize = (kb = 0) => kb >= 1024 ? `${(kb / 1024).toFixed(1)} MB` : `${Math.round(kb)} KB`;
|
|
1841
|
-
const FileCard = ({ file, onAction, busy }) => {
|
|
1841
|
+
const FileCard = ({ file, onAction, busy, drive }) => {
|
|
1842
1842
|
const c = useThemeColors();
|
|
1843
1843
|
const thumb2 = file.formats?.thumbnail?.url ?? file.formats?.small?.url ?? file.url;
|
|
1844
1844
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -1877,7 +1877,8 @@ const FileCard = ({ file, onAction, busy }) => {
|
|
|
1877
1877
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
1878
1878
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: formatSize(file.size) }),
|
|
1879
1879
|
isGenerated(file) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { children: "Generada" }),
|
|
1880
|
-
busy && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { active: true, children: busy })
|
|
1880
|
+
busy && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { active: true, children: busy }),
|
|
1881
|
+
drive?.label && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: drive.tone, title: drive.title, children: drive.label })
|
|
1881
1882
|
] })
|
|
1882
1883
|
] }),
|
|
1883
1884
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Menu.Root, { children: [
|
|
@@ -1889,6 +1890,8 @@ const FileCard = ({ file, onAction, busy }) => {
|
|
|
1889
1890
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Menu.Item, { onSelect: () => onAction("replace", file), children: "Reemplazar" }),
|
|
1890
1891
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Menu.Item, { onSelect: () => onAction("move", file), children: "Mover a…" }),
|
|
1891
1892
|
canRemoveBackground(file) && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Menu.Item, { onSelect: () => onAction("removeBackground", file), disabled: !!busy, children: "Quitar fondo" }),
|
|
1893
|
+
drive?.action === "mark" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Menu.Item, { onSelect: () => onAction("syncDrive", file), children: "Sincronizar con Drive" }),
|
|
1894
|
+
drive?.action === "unmark" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Menu.Item, { onSelect: () => onAction("unsyncDrive", file), children: "Dejar de sincronizar" }),
|
|
1892
1895
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Menu.Item, { onSelect: () => onAction("delete", file), variant: "danger", children: "Eliminar" })
|
|
1893
1896
|
] })
|
|
1894
1897
|
] })
|
|
@@ -1897,10 +1900,255 @@ const FileCard = ({ file, onAction, busy }) => {
|
|
|
1897
1900
|
}
|
|
1898
1901
|
);
|
|
1899
1902
|
};
|
|
1903
|
+
const BASE$1 = "/strapi-content-generator-gemini/drive";
|
|
1904
|
+
const FOLDER = "application/vnd.google-apps.folder";
|
|
1905
|
+
const NATIVE_TYPES = {
|
|
1906
|
+
"application/vnd.google-apps.document": "Documento de Google",
|
|
1907
|
+
"application/vnd.google-apps.spreadsheet": "Hoja de cálculo de Google",
|
|
1908
|
+
"application/vnd.google-apps.presentation": "Presentación de Google",
|
|
1909
|
+
"application/vnd.google-apps.form": "Formulario de Google",
|
|
1910
|
+
"application/vnd.google-apps.drawing": "Dibujo de Google",
|
|
1911
|
+
"application/vnd.google-apps.shortcut": "Acceso directo"
|
|
1912
|
+
};
|
|
1913
|
+
const typeLabel = (mime) => {
|
|
1914
|
+
if (mime === FOLDER) return "Carpeta";
|
|
1915
|
+
if (NATIVE_TYPES[mime]) return NATIVE_TYPES[mime];
|
|
1916
|
+
if (mime.startsWith("application/vnd.google-apps.")) return "Archivo de Google";
|
|
1917
|
+
if (mime === "application/pdf") return "PDF";
|
|
1918
|
+
if (mime.startsWith("image/")) return "Imagen";
|
|
1919
|
+
if (mime.startsWith("video/")) return "Video";
|
|
1920
|
+
if (mime.startsWith("audio/")) return "Audio";
|
|
1921
|
+
return "Archivo";
|
|
1922
|
+
};
|
|
1923
|
+
const TypeIcon = ({ mime }) => mime === FOLDER ? /* @__PURE__ */ jsxRuntime.jsx(icons.Folder, { width: 16, height: 16, "aria-hidden": true }) : mime === "application/pdf" ? /* @__PURE__ */ jsxRuntime.jsx(icons.FilePdf, { width: 16, height: 16, "aria-hidden": true }) : /* @__PURE__ */ jsxRuntime.jsx(icons.File, { width: 16, height: 16, "aria-hidden": true });
|
|
1924
|
+
const formatDate = (iso) => iso ? new Date(iso).toLocaleString("es-MX", { dateStyle: "medium", timeStyle: "short", timeZone: "America/Mexico_City" }) : "—";
|
|
1925
|
+
const confirmText = (item, count) => {
|
|
1926
|
+
const parts = [`«${item.name}» irá a la papelera de Drive, donde se puede recuperar durante 30 días.`];
|
|
1927
|
+
if (item.mimeType === FOLDER && count) {
|
|
1928
|
+
const total = count.files + count.folders;
|
|
1929
|
+
parts.push(
|
|
1930
|
+
total === 0 ? "La carpeta está vacía." : `Contiene ${count.truncated ? "más de " : ""}${total.toLocaleString("es-MX")} ${total === 1 ? "elemento" : "elementos"} (${count.files} archivos y ${count.folders} carpetas), que también se van.`
|
|
1931
|
+
);
|
|
1932
|
+
}
|
|
1933
|
+
if (item.synced || count?.synced) parts.push("Está sincronizado: también se borrará de la Biblioteca del CMS.");
|
|
1934
|
+
return parts.join(" ");
|
|
1935
|
+
};
|
|
1936
|
+
const serverMessage$1 = (error, fallback) => error?.response?.data?.error?.message ?? (error instanceof Error && error.message ? error.message : fallback);
|
|
1937
|
+
const useDriveBrowserApi = () => {
|
|
1938
|
+
const { get, del } = admin.useFetchClient();
|
|
1939
|
+
return react.useMemo(
|
|
1940
|
+
() => ({
|
|
1941
|
+
browse: async (folderId, pageToken) => {
|
|
1942
|
+
try {
|
|
1943
|
+
return (await get(`${BASE$1}/folders/${encodeURIComponent(folderId)}`, { params: pageToken ? { pageToken } : {} })).data;
|
|
1944
|
+
} catch (error) {
|
|
1945
|
+
throw new Error(serverMessage$1(error, "No se pudo leer Google Drive."));
|
|
1946
|
+
}
|
|
1947
|
+
},
|
|
1948
|
+
count: async (id) => (await get(`${BASE$1}/items/${encodeURIComponent(id)}/count`)).data,
|
|
1949
|
+
trash: async (id) => {
|
|
1950
|
+
try {
|
|
1951
|
+
await del(`${BASE$1}/items/${encodeURIComponent(id)}`);
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
throw new Error(serverMessage$1(error, "No se pudo mandar a la papelera."));
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
}),
|
|
1957
|
+
[get, del]
|
|
1958
|
+
);
|
|
1959
|
+
};
|
|
1960
|
+
const DriveTab = ({ api: injected, notify }) => {
|
|
1961
|
+
const fallbackApi = useDriveBrowserApi();
|
|
1962
|
+
const api = injected ?? fallbackApi;
|
|
1963
|
+
const { toggleNotification } = admin.useNotification();
|
|
1964
|
+
const say = notify ?? ((type, message) => toggleNotification({ type, message }));
|
|
1965
|
+
const [folderId, setFolderId] = react.useState("root");
|
|
1966
|
+
const [page, setPage] = react.useState(null);
|
|
1967
|
+
const [error, setError] = react.useState(null);
|
|
1968
|
+
const [loading2, setLoading] = react.useState(true);
|
|
1969
|
+
const [loadingMore, setLoadingMore] = react.useState(false);
|
|
1970
|
+
const [doomed, setDoomed] = react.useState(null);
|
|
1971
|
+
const load = react.useCallback(async () => {
|
|
1972
|
+
setLoading(true);
|
|
1973
|
+
setError(null);
|
|
1974
|
+
try {
|
|
1975
|
+
setPage(await api.browse(folderId));
|
|
1976
|
+
} catch (err) {
|
|
1977
|
+
setError(err instanceof Error ? err.message : "No se pudo leer Google Drive.");
|
|
1978
|
+
} finally {
|
|
1979
|
+
setLoading(false);
|
|
1980
|
+
}
|
|
1981
|
+
}, [api, folderId]);
|
|
1982
|
+
react.useEffect(() => {
|
|
1983
|
+
load();
|
|
1984
|
+
}, [load]);
|
|
1985
|
+
const more = async () => {
|
|
1986
|
+
if (!page?.nextPageToken) return;
|
|
1987
|
+
setLoadingMore(true);
|
|
1988
|
+
try {
|
|
1989
|
+
const next = await api.browse(folderId, page.nextPageToken);
|
|
1990
|
+
setPage({ ...next, items: [...page.items, ...next.items] });
|
|
1991
|
+
} finally {
|
|
1992
|
+
setLoadingMore(false);
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
const askDelete = async (item) => {
|
|
1996
|
+
setDoomed({ item, count: null });
|
|
1997
|
+
if (item.mimeType === FOLDER) {
|
|
1998
|
+
try {
|
|
1999
|
+
const count = await api.count(item.id);
|
|
2000
|
+
setDoomed((d) => d?.item.id === item.id ? { item, count } : d);
|
|
2001
|
+
} catch {
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
};
|
|
2005
|
+
const confirmDelete = async () => {
|
|
2006
|
+
if (!doomed) return;
|
|
2007
|
+
const { item } = doomed;
|
|
2008
|
+
setDoomed(null);
|
|
2009
|
+
try {
|
|
2010
|
+
await api.trash(item.id);
|
|
2011
|
+
say("success", `«${item.name}» se mandó a la papelera de Drive.`);
|
|
2012
|
+
await load();
|
|
2013
|
+
} catch (err) {
|
|
2014
|
+
say("danger", err instanceof Error ? err.message : "No se pudo mandar a la papelera.");
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
if (error) return /* @__PURE__ */ jsxRuntime.jsx(EmptyNote, { children: error });
|
|
2018
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
2019
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { "data-testid": "drive-breadcrumb", gap: 1, alignItems: "center", wrap: "wrap", paddingBottom: 4, children: [
|
|
2020
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: () => setFolderId("root"), children: "Mi unidad" }),
|
|
2021
|
+
(page?.path ?? []).map((p, i, all) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
|
|
2022
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ChevronRight, { width: 10, height: 10, "aria-hidden": true }),
|
|
2023
|
+
i === all.length - 1 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", children: p.name }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "ghost", size: "S", onClick: () => setFolderId(p.id), children: p.name })
|
|
2024
|
+
] }, p.id))
|
|
2025
|
+
] }),
|
|
2026
|
+
loading2 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Cargando Google Drive…" }) }) : !page?.items.length ? /* @__PURE__ */ jsxRuntime.jsx(EmptyNote, { children: "Esta carpeta de Drive está vacía." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2027
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount: 6, rowCount: page.items.length + 1, children: [
|
|
2028
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
2029
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Nombre" }) }),
|
|
2030
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Tipo" }) }),
|
|
2031
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Modificado" }) }),
|
|
2032
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Tamaño" }) }),
|
|
2033
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: "Estado" }) }),
|
|
2034
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { children: "Acciones" }) })
|
|
2035
|
+
] }) }),
|
|
2036
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: page.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
2037
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { style: { maxWidth: 420, whiteSpace: "normal" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
|
|
2038
|
+
/* @__PURE__ */ jsxRuntime.jsx(TypeIcon, { mime: item.mimeType }),
|
|
2039
|
+
item.mimeType === FOLDER ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.TextButton, { onClick: () => setFolderId(item.id), style: { textAlign: "left", height: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", textColor: "neutral800", style: { overflowWrap: "anywhere" }, children: item.name }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", style: { overflowWrap: "anywhere" }, children: item.name })
|
|
2040
|
+
] }) }),
|
|
2041
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral700", children: typeLabel(item.mimeType) }) }),
|
|
2042
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral700", style: { whiteSpace: "nowrap" }, children: formatDate(item.modifiedTime) }) }),
|
|
2043
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral700", children: item.size === null ? "—" : formatSize(item.size / 1e3) }) }),
|
|
2044
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: item.synced && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: "success", children: "Sincronizado" }) }),
|
|
2045
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, justifyContent: "end", children: [
|
|
2046
|
+
item.webViewLink && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2047
|
+
designSystem.LinkButton,
|
|
2048
|
+
{
|
|
2049
|
+
variant: "tertiary",
|
|
2050
|
+
size: "S",
|
|
2051
|
+
href: item.webViewLink,
|
|
2052
|
+
target: "_blank",
|
|
2053
|
+
rel: "noreferrer noopener",
|
|
2054
|
+
endIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.ExternalLink, {}),
|
|
2055
|
+
children: "Abrir en Drive"
|
|
2056
|
+
}
|
|
2057
|
+
),
|
|
2058
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: `Eliminar ${item.name}`, variant: "ghost", onClick: () => askDelete(item), children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) })
|
|
2059
|
+
] }) })
|
|
2060
|
+
] }, item.id)) })
|
|
2061
|
+
] }),
|
|
2062
|
+
page.nextPageToken && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", paddingTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: more, loading: loadingMore, children: "Mostrar más" }) })
|
|
2063
|
+
] }),
|
|
2064
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2065
|
+
ConfirmDialog,
|
|
2066
|
+
{
|
|
2067
|
+
open: !!doomed,
|
|
2068
|
+
title: "¿Mandar a la papelera de Drive?",
|
|
2069
|
+
confirmLabel: "Mandar a la papelera",
|
|
2070
|
+
onClose: () => setDoomed(null),
|
|
2071
|
+
onConfirm: confirmDelete,
|
|
2072
|
+
children: doomed ? confirmText(doomed.item, doomed.count) : ""
|
|
2073
|
+
}
|
|
2074
|
+
)
|
|
2075
|
+
] });
|
|
2076
|
+
};
|
|
2077
|
+
const BASE = "/strapi-content-generator-gemini/drive";
|
|
2078
|
+
const POLL_MS = 1e4;
|
|
2079
|
+
const LABELS = {
|
|
2080
|
+
synced: { label: "Drive", tone: "success" },
|
|
2081
|
+
pending: { label: "Drive: pendiente", tone: "neutral" },
|
|
2082
|
+
error: { label: "Drive: error", tone: "danger" },
|
|
2083
|
+
skipped: { label: "Drive: omitido", tone: "warning" }
|
|
2084
|
+
};
|
|
2085
|
+
const driveView = (links, key, parentInside) => {
|
|
2086
|
+
if (!links?.connected) return null;
|
|
2087
|
+
const item = links.items[key];
|
|
2088
|
+
if (item?.inside) {
|
|
2089
|
+
return { ...LABELS[item.status], title: item.error ?? void 0, action: item.root ? "unmark" : null };
|
|
2090
|
+
}
|
|
2091
|
+
if (parentInside) return { ...LABELS.pending, action: null };
|
|
2092
|
+
return { label: null, tone: "neutral", action: "mark" };
|
|
2093
|
+
};
|
|
2094
|
+
const serverMessage = (error, fallback) => error?.response?.data?.error?.message ?? fallback;
|
|
2095
|
+
const useDriveSync = (watching = false) => {
|
|
2096
|
+
const { get, post, del } = admin.useFetchClient();
|
|
2097
|
+
const [links, setLinks] = react.useState(null);
|
|
2098
|
+
const refresh = react.useCallback(async () => {
|
|
2099
|
+
try {
|
|
2100
|
+
const { data } = await get(`${BASE}/links`);
|
|
2101
|
+
setLinks(data);
|
|
2102
|
+
} catch {
|
|
2103
|
+
setLinks(null);
|
|
2104
|
+
}
|
|
2105
|
+
}, [get]);
|
|
2106
|
+
react.useEffect(() => {
|
|
2107
|
+
refresh();
|
|
2108
|
+
}, [refresh]);
|
|
2109
|
+
const busy = !!links?.connected && (watching || links.pending > 0 || Object.values(links.items).some((i) => i.status === "pending"));
|
|
2110
|
+
react.useEffect(() => {
|
|
2111
|
+
if (!busy) return;
|
|
2112
|
+
const timer = setInterval(refresh, POLL_MS);
|
|
2113
|
+
return () => clearInterval(timer);
|
|
2114
|
+
}, [busy, refresh]);
|
|
2115
|
+
react.useEffect(() => {
|
|
2116
|
+
const onVisible = () => document.visibilityState === "visible" && refresh();
|
|
2117
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
2118
|
+
return () => document.removeEventListener("visibilitychange", onVisible);
|
|
2119
|
+
}, [refresh]);
|
|
2120
|
+
return react.useMemo(
|
|
2121
|
+
() => ({
|
|
2122
|
+
links,
|
|
2123
|
+
refresh,
|
|
2124
|
+
async mark(cmsType, cmsId) {
|
|
2125
|
+
try {
|
|
2126
|
+
await post(`${BASE}/links`, { cmsType, cmsId });
|
|
2127
|
+
} catch (error) {
|
|
2128
|
+
throw new Error(serverMessage(error, "No se pudo sincronizar con Drive."));
|
|
2129
|
+
} finally {
|
|
2130
|
+
await refresh();
|
|
2131
|
+
}
|
|
2132
|
+
},
|
|
2133
|
+
async unmark(cmsType, cmsId) {
|
|
2134
|
+
try {
|
|
2135
|
+
await del(`${BASE}/links/${cmsType}/${cmsId}`);
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
throw new Error(serverMessage(error, "No se pudo dejar de sincronizar."));
|
|
2138
|
+
} finally {
|
|
2139
|
+
await refresh();
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
}),
|
|
2143
|
+
[links, refresh, post, del]
|
|
2144
|
+
);
|
|
2145
|
+
};
|
|
1900
2146
|
const status = (err) => err?.response?.status ?? err?.status;
|
|
1901
2147
|
const LibraryTab = () => {
|
|
1902
2148
|
const c = useThemeColors();
|
|
1903
2149
|
const { api, notify } = useLibraryApi();
|
|
2150
|
+
const [drivePending, setDrivePending] = react.useState(false);
|
|
2151
|
+
const drive = useDriveSync(drivePending);
|
|
1904
2152
|
const [tree, setTree] = react.useState([]);
|
|
1905
2153
|
const [counts, setCounts] = react.useState({});
|
|
1906
2154
|
const [current, setCurrent] = react.useState(null);
|
|
@@ -1944,7 +2192,20 @@ const LibraryTab = () => {
|
|
|
1944
2192
|
react.useEffect(() => {
|
|
1945
2193
|
loadFiles();
|
|
1946
2194
|
}, [loadFiles]);
|
|
1947
|
-
const refresh = () => Promise.all([loadTree(), loadFiles()]);
|
|
2195
|
+
const refresh = () => Promise.all([loadTree(), loadFiles(), drive.refresh()]);
|
|
2196
|
+
const toggleDrive = async (mode, cmsType, cmsId) => {
|
|
2197
|
+
try {
|
|
2198
|
+
if (mode === "mark") {
|
|
2199
|
+
await drive.mark(cmsType, cmsId);
|
|
2200
|
+
notify("success", "Se sincronizará con Drive en unos segundos.");
|
|
2201
|
+
} else {
|
|
2202
|
+
await drive.unmark(cmsType, cmsId);
|
|
2203
|
+
notify("success", "Dejó de sincronizarse. No se borró nada en Drive ni aquí.");
|
|
2204
|
+
}
|
|
2205
|
+
} catch (err) {
|
|
2206
|
+
notify("danger", err instanceof Error ? err.message : "No se pudo completar la operación con Drive.");
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
1948
2209
|
const run = async (action, ok) => {
|
|
1949
2210
|
try {
|
|
1950
2211
|
await action();
|
|
@@ -2001,12 +2262,24 @@ const LibraryTab = () => {
|
|
|
2001
2262
|
if (action === "rename") return setDialog({ kind: "renameFile", file });
|
|
2002
2263
|
if (action === "move") return setDialog({ kind: "moveFile", file });
|
|
2003
2264
|
if (action === "delete") return setDialog({ kind: "deleteFile", file });
|
|
2265
|
+
if (action === "syncDrive") return toggleDrive("mark", "file", file.id);
|
|
2266
|
+
if (action === "unsyncDrive") return toggleDrive("unmark", "file", file.id);
|
|
2004
2267
|
};
|
|
2005
2268
|
const validateFolder = (parent, except) => (name) => cleanFolderName(name) !== except && nameTaken(tree, parent, name) ? "Ya existe una carpeta con ese nombre aquí." : null;
|
|
2269
|
+
const pendingShown = (() => {
|
|
2270
|
+
const inside = current !== null && !!drive.links?.items[`folder:${current}`]?.inside;
|
|
2271
|
+
const isPending = (key) => driveView(drive.links, key, inside)?.label === "Drive: pendiente";
|
|
2272
|
+
return files.some((f) => isPending(`file:${f.id}`)) || childrenOf(tree, current).some((f) => isPending(`folder:${f.id}`));
|
|
2273
|
+
})();
|
|
2274
|
+
react.useEffect(() => {
|
|
2275
|
+
setDrivePending(pendingShown);
|
|
2276
|
+
}, [pendingShown]);
|
|
2006
2277
|
if (forbidden) {
|
|
2007
2278
|
return /* @__PURE__ */ jsxRuntime.jsx(EmptyNote, { children: "Para usar la biblioteca necesitas permiso de la Media Library de Strapi. Pídelo a un administrador." });
|
|
2008
2279
|
}
|
|
2009
2280
|
const subfolders = childrenOf(tree, current);
|
|
2281
|
+
const currentInside = current !== null && !!drive.links?.items[`folder:${current}`]?.inside;
|
|
2282
|
+
const folderDrive = current !== null ? driveView(drive.links, `folder:${current}`, !!(folder && path.length > 1 && drive.links?.items[`folder:${path[path.length - 2].id}`]?.inside)) : null;
|
|
2010
2283
|
return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 4, alignItems: "stretch", children: [
|
|
2011
2284
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "280px", shrink: 0, background: "neutral0", padding: 3, hasRadius: true, style: { maxHeight: "calc(100vh - 220px)", overflowY: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2012
2285
|
FolderTree,
|
|
@@ -2053,6 +2326,9 @@ const LibraryTab = () => {
|
|
|
2053
2326
|
] }, f.id))
|
|
2054
2327
|
] }),
|
|
2055
2328
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
|
|
2329
|
+
folder && folderDrive?.label && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { variant: folderDrive.tone, title: folderDrive.title, children: folderDrive.label }),
|
|
2330
|
+
folder && folderDrive?.action === "mark" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: () => toggleDrive("mark", "folder", folder.id), children: "Sincronizar con Drive" }),
|
|
2331
|
+
folder && folderDrive?.action === "unmark" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: () => toggleDrive("unmark", "folder", folder.id), children: "Dejar de sincronizar" }),
|
|
2056
2332
|
folder && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2057
2333
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {}), onClick: () => setDialog({ kind: "renameFolder" }), children: "Renombrar carpeta" }),
|
|
2058
2334
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "danger-light", startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}), onClick: () => setDialog({ kind: "deleteFolder" }), children: "Eliminar carpeta" })
|
|
@@ -2077,11 +2353,15 @@ const LibraryTab = () => {
|
|
|
2077
2353
|
children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
|
|
2078
2354
|
/* @__PURE__ */ jsxRuntime.jsx(icons.Folder, { width: 16, height: 16, "aria-hidden": true }),
|
|
2079
2355
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { ellipsis: true, style: { flex: 1 }, children: f.name }),
|
|
2356
|
+
(() => {
|
|
2357
|
+
const view = driveView(drive.links, `folder:${f.id}`, currentInside);
|
|
2358
|
+
return view?.label ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { size: "S", variant: view.tone, title: view.title, children: view.label }) : null;
|
|
2359
|
+
})(),
|
|
2080
2360
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: counts[f.id]?.files ?? 0 })
|
|
2081
2361
|
] })
|
|
2082
2362
|
}
|
|
2083
2363
|
) }, f.id)) }),
|
|
2084
|
-
loading2 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Cargando archivos…" }) }) : files.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(EmptyNote, { children: subfolders.length ? "No hay archivos sueltos en esta carpeta." : "Carpeta vacía. Arrastra archivos aquí o usa «Subir archivos»." }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Root, { gap: 4, children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 3, m: 4, s: 6, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(FileCard, { file: f, onAction: onFileAction, busy: bgBusy[f.id] }) }, f.id)) }),
|
|
2364
|
+
loading2 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Cargando archivos…" }) }) : files.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(EmptyNote, { children: subfolders.length ? "No hay archivos sueltos en esta carpeta." : "Carpeta vacía. Arrastra archivos aquí o usa «Subir archivos»." }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Root, { gap: 4, children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 3, m: 4, s: 6, xs: 12, children: /* @__PURE__ */ jsxRuntime.jsx(FileCard, { file: f, onAction: onFileAction, busy: bgBusy[f.id], drive: driveView(drive.links, `file:${f.id}`, currentInside) }) }, f.id)) }),
|
|
2085
2365
|
dragOver && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { position: "absolute", justifyContent: "center", background: "primary100", hasRadius: true, style: { inset: 0, border: `2px dashed ${c.primary600}`, opacity: 0.95 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "beta", textColor: "primary700", children: [
|
|
2086
2366
|
"Suelta para subir a «",
|
|
2087
2367
|
folder?.name ?? ROOT_LABEL,
|
|
@@ -2763,6 +3043,15 @@ const editorReducer = (state, action) => {
|
|
|
2763
3043
|
}
|
|
2764
3044
|
}
|
|
2765
3045
|
};
|
|
3046
|
+
const ORIGIN_URL = "/strapi-content-generator-gemini/drive/origin";
|
|
3047
|
+
const recordTemplateOrigin = async (post, files, template) => {
|
|
3048
|
+
for (const file of files) {
|
|
3049
|
+
try {
|
|
3050
|
+
await post(ORIGIN_URL, { fileId: file.id, templateDocumentId: template.documentId ?? template.id, templateName: template.name });
|
|
3051
|
+
} catch {
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
};
|
|
2766
3055
|
const useEditorIO = () => {
|
|
2767
3056
|
const { post } = admin.useFetchClient();
|
|
2768
3057
|
const { toggleNotification } = admin.useNotification();
|
|
@@ -2795,7 +3084,8 @@ const useEditorIO = () => {
|
|
|
2795
3084
|
if (!node) return false;
|
|
2796
3085
|
try {
|
|
2797
3086
|
const blob = await renderPiece(node, template, "png");
|
|
2798
|
-
await api.saveBlob(blob, /\.png$/i.test(name) ? name : `${name}.png`, folderId);
|
|
3087
|
+
const saved = await api.saveBlob(blob, /\.png$/i.test(name) ? name : `${name}.png`, folderId);
|
|
3088
|
+
await recordTemplateOrigin((u, body) => post(u, body), saved, template);
|
|
2799
3089
|
toggleNotification({ type: "success", message: "Pieza guardada en la biblioteca." });
|
|
2800
3090
|
return true;
|
|
2801
3091
|
} catch {
|
|
@@ -2803,7 +3093,7 @@ const useEditorIO = () => {
|
|
|
2803
3093
|
return false;
|
|
2804
3094
|
}
|
|
2805
3095
|
},
|
|
2806
|
-
[api, toggleNotification]
|
|
3096
|
+
[api, post, toggleNotification]
|
|
2807
3097
|
);
|
|
2808
3098
|
return { loadImage: loadImage2, exportAs, saveToLibrary };
|
|
2809
3099
|
};
|
|
@@ -5248,7 +5538,8 @@ const TemplatesTab = () => {
|
|
|
5248
5538
|
const STUDIO_TABS = [
|
|
5249
5539
|
{ value: "generator", label: "Generador" },
|
|
5250
5540
|
{ value: "templates", label: "Plantillas" },
|
|
5251
|
-
{ value: "library", label: "Imágenes y documentos" }
|
|
5541
|
+
{ value: "library", label: "Imágenes y documentos" },
|
|
5542
|
+
{ value: "drive", label: "Google Drive" }
|
|
5252
5543
|
];
|
|
5253
5544
|
const PANEL = { height: "100%", overflowY: "auto" };
|
|
5254
5545
|
const isStudioTab = (v) => STUDIO_TABS.some((t) => t.value === v);
|
|
@@ -5276,7 +5567,8 @@ const ImageStudioTabs = () => {
|
|
|
5276
5567
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { flex: "1", style: { minHeight: 0 }, children: [
|
|
5277
5568
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "generator", style: PANEL, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, children: /* @__PURE__ */ jsxRuntime.jsx(GeneratorTab, {}) }) }),
|
|
5278
5569
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "templates", style: PANEL, children: /* @__PURE__ */ jsxRuntime.jsx(TemplatesTab, {}) }),
|
|
5279
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "library", style: PANEL, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, children: /* @__PURE__ */ jsxRuntime.jsx(LibraryTab, {}) }) })
|
|
5570
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "library", style: PANEL, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, children: /* @__PURE__ */ jsxRuntime.jsx(LibraryTab, {}) }) }),
|
|
5571
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "drive", style: PANEL, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 6, children: tab === "drive" && /* @__PURE__ */ jsxRuntime.jsx(DriveTab, {}) }) })
|
|
5280
5572
|
] })
|
|
5281
5573
|
]
|
|
5282
5574
|
}
|