@micelio/strapi-plugin-core 0.1.0 → 0.2.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 (54) hide show
  1. package/README.md +5 -3
  2. package/dist/admin/MicelioLayout-Bmu5xCw9.js +1023 -0
  3. package/dist/admin/MicelioLayout-DMMsKnMo.mjs +1021 -0
  4. package/dist/admin/PreviewHeaderAction-BaduMvYy.mjs +61 -0
  5. package/dist/admin/PreviewHeaderAction-CGHycv4I.js +61 -0
  6. package/dist/admin/{en-D-T8z3qh.js → en-BDVuq0fN.js} +37 -1
  7. package/dist/admin/{en-C2O-S2ym.mjs → en-Bahc6y6N.mjs} +37 -1
  8. package/dist/admin/{es-RV0vkOHh.js → es-DStBGkm7.js} +37 -1
  9. package/dist/admin/{es-CQ2tv9N8.mjs → es-nUHBIoZG.mjs} +37 -1
  10. package/dist/admin/{index-D3TGbaQ7.js → index-D_Dbvo2F.js} +8 -2
  11. package/dist/admin/{index-CiSujTES.mjs → index-Dwr15ZsB.mjs} +8 -2
  12. package/dist/admin/index.js +1 -1
  13. package/dist/admin/index.mjs +1 -1
  14. package/dist/admin/permissions-BAI0qDr9.js +24 -0
  15. package/dist/admin/permissions-CYirM30e.mjs +25 -0
  16. package/dist/admin/src/components/EyeIcon.d.ts +8 -0
  17. package/dist/admin/src/components/PreviewFrame.d.ts +47 -0
  18. package/dist/admin/src/components/PreviewHeaderAction.d.ts +28 -0
  19. package/dist/admin/src/hooks/useEntry.d.ts +31 -0
  20. package/dist/admin/src/hooks/usePreviewSource.d.ts +31 -0
  21. package/dist/admin/src/pages/MicelioLayout.d.ts +2 -1
  22. package/dist/admin/src/pages/PreviewPage.d.ts +2 -0
  23. package/dist/admin/src/permissions.d.ts +5 -0
  24. package/dist/admin/src/translations/en.json.d.ts +37 -1
  25. package/dist/admin/src/utils/fetchError.d.ts +2 -0
  26. package/dist/admin/src/utils/previewAction.d.ts +22 -0
  27. package/dist/admin/src/utils/previewPane.d.ts +59 -0
  28. package/dist/server/index.js +796 -105
  29. package/dist/server/index.mjs +796 -105
  30. package/dist/server/src/constants/preview.d.ts +2 -0
  31. package/dist/server/src/constants/taxonomy.d.ts +13 -0
  32. package/dist/server/src/content-types/category/schema.json.d.ts +78 -0
  33. package/dist/server/src/content-types/index.d.ts +125 -2
  34. package/dist/server/src/content-types/preview-credential/schema.json.d.ts +7 -6
  35. package/dist/server/src/content-types/tag/schema.json.d.ts +46 -0
  36. package/dist/server/src/controllers/index.d.ts +6 -0
  37. package/dist/server/src/editorial/transitions.d.ts +5 -2
  38. package/dist/server/src/index.d.ts +140 -2
  39. package/dist/server/src/migrations/rename-legacy-uids.d.ts +26 -0
  40. package/dist/server/src/migrations/site-backfill.d.ts +13 -0
  41. package/dist/server/src/preview/credentials.d.ts +7 -5
  42. package/dist/server/src/preview/draft-query.d.ts +62 -0
  43. package/dist/server/src/preview/revocation.d.ts +9 -1
  44. package/dist/server/src/preview/shortcut.d.ts +24 -0
  45. package/dist/server/src/routes/index.d.ts +2 -0
  46. package/dist/server/src/routes/taxonomy.d.ts +12 -0
  47. package/dist/server/src/services/index.d.ts +6 -0
  48. package/dist/server/src/services/preview.d.ts +25 -6
  49. package/dist/server/src/site-scope/documents.d.ts +13 -3
  50. package/dist/server/src/types/hooks.d.ts +5 -4
  51. package/dist/server/src/types/preview.d.ts +35 -10
  52. package/package.json +4 -3
  53. package/dist/admin/MicelioLayout-3sKGPS_0.mjs +0 -443
  54. package/dist/admin/MicelioLayout-DXzRS4l3.js +0 -443
@@ -0,0 +1,61 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { unstable_useContentManagerContext, useAuth, useForm } from "@strapi/strapi/admin";
3
+ import { useNavigate } from "react-router-dom";
4
+ import { u as useMessages, P as PREVIEW_PERMISSION } from "./permissions-CYirM30e.mjs";
5
+ import { P as PLUGIN_ID } from "./index-Dwr15ZsB.mjs";
6
+ function hasPreview(schema) {
7
+ if (schema?.pluginOptions?.micelio?.siteScoped !== true) return false;
8
+ return Object.values(schema.attributes ?? {}).some(
9
+ (attribute) => attribute?.type === "dynamiczone"
10
+ );
11
+ }
12
+ function previewPagePath(uid, documentId, locale) {
13
+ const page = `/plugins/${PLUGIN_ID}/preview/${encodeURIComponent(uid)}/${encodeURIComponent(documentId)}`;
14
+ return locale ? `${page}?locale=${encodeURIComponent(locale)}` : page;
15
+ }
16
+ const EyeIcon = (props) => /* @__PURE__ */ jsxs(
17
+ "svg",
18
+ {
19
+ xmlns: "http://www.w3.org/2000/svg",
20
+ width: "1.6rem",
21
+ height: "1.6rem",
22
+ viewBox: "0 0 24 24",
23
+ fill: "none",
24
+ stroke: "currentColor",
25
+ strokeWidth: "1.75",
26
+ strokeLinecap: "round",
27
+ strokeLinejoin: "round",
28
+ "aria-hidden": "true",
29
+ focusable: "false",
30
+ ...props,
31
+ children: [
32
+ /* @__PURE__ */ jsx("path", { style: { fill: "none" }, d: "M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z" }),
33
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
34
+ ]
35
+ }
36
+ );
37
+ function PreviewHeaderAction({
38
+ collectionType,
39
+ documentId,
40
+ document
41
+ }) {
42
+ const { t } = useMessages();
43
+ const navigate = useNavigate();
44
+ const { contentType } = unstable_useContentManagerContext();
45
+ const permissions = useAuth("PreviewHeaderAction", (state) => state.permissions);
46
+ const isModified = useForm("PreviewHeaderAction", (state) => state.modified);
47
+ const mayPreview = permissions.some(({ action }) => action === PREVIEW_PERMISSION.action);
48
+ if (collectionType !== "collection-types" || !documentId || !mayPreview || !hasPreview(contentType)) {
49
+ return null;
50
+ }
51
+ const uid = contentType?.uid ?? "";
52
+ return {
53
+ label: t(isModified ? "action.preview.unsaved" : "action.preview"),
54
+ icon: /* @__PURE__ */ jsx(EyeIcon, {}),
55
+ disabled: Boolean(isModified),
56
+ onClick: () => navigate(previewPagePath(uid, documentId, document?.locale ?? null))
57
+ };
58
+ }
59
+ export {
60
+ PreviewHeaderAction
61
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const admin = require("@strapi/strapi/admin");
5
+ const reactRouterDom = require("react-router-dom");
6
+ const permissions = require("./permissions-BAI0qDr9.js");
7
+ const index = require("./index-D_Dbvo2F.js");
8
+ function hasPreview(schema) {
9
+ if (schema?.pluginOptions?.micelio?.siteScoped !== true) return false;
10
+ return Object.values(schema.attributes ?? {}).some(
11
+ (attribute) => attribute?.type === "dynamiczone"
12
+ );
13
+ }
14
+ function previewPagePath(uid, documentId, locale) {
15
+ const page = `/plugins/${index.PLUGIN_ID}/preview/${encodeURIComponent(uid)}/${encodeURIComponent(documentId)}`;
16
+ return locale ? `${page}?locale=${encodeURIComponent(locale)}` : page;
17
+ }
18
+ const EyeIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
19
+ "svg",
20
+ {
21
+ xmlns: "http://www.w3.org/2000/svg",
22
+ width: "1.6rem",
23
+ height: "1.6rem",
24
+ viewBox: "0 0 24 24",
25
+ fill: "none",
26
+ stroke: "currentColor",
27
+ strokeWidth: "1.75",
28
+ strokeLinecap: "round",
29
+ strokeLinejoin: "round",
30
+ "aria-hidden": "true",
31
+ focusable: "false",
32
+ ...props,
33
+ children: [
34
+ /* @__PURE__ */ jsxRuntime.jsx("path", { style: { fill: "none" }, d: "M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7S2 12 2 12z" }),
35
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" })
36
+ ]
37
+ }
38
+ );
39
+ function PreviewHeaderAction({
40
+ collectionType,
41
+ documentId,
42
+ document
43
+ }) {
44
+ const { t } = permissions.useMessages();
45
+ const navigate = reactRouterDom.useNavigate();
46
+ const { contentType } = admin.unstable_useContentManagerContext();
47
+ const permissions$1 = admin.useAuth("PreviewHeaderAction", (state) => state.permissions);
48
+ const isModified = admin.useForm("PreviewHeaderAction", (state) => state.modified);
49
+ const mayPreview = permissions$1.some(({ action }) => action === permissions.PREVIEW_PERMISSION.action);
50
+ if (collectionType !== "collection-types" || !documentId || !mayPreview || !hasPreview(contentType)) {
51
+ return null;
52
+ }
53
+ const uid = contentType?.uid ?? "";
54
+ return {
55
+ label: t(isModified ? "action.preview.unsaved" : "action.preview"),
56
+ icon: /* @__PURE__ */ jsxRuntime.jsx(EyeIcon, {}),
57
+ disabled: Boolean(isModified),
58
+ onClick: () => navigate(previewPagePath(uid, documentId, document?.locale ?? null))
59
+ };
60
+ }
61
+ exports.PreviewHeaderAction = PreviewHeaderAction;
@@ -92,6 +92,42 @@ const en = {
92
92
  "status.plugins.fix": "Fix",
93
93
  "status.diagnostics.copy": "Copy diagnostics",
94
94
  "status.diagnostics.copied": "The diagnostics were copied.",
95
- "status.diagnostics.failed": "The diagnostics could not be copied."
95
+ "status.diagnostics.failed": "The diagnostics could not be copied.",
96
+ "preview.title": "Preview",
97
+ "preview.page": "Preview of {title}",
98
+ "preview.subtitle": "The saved draft, as the site renders it.",
99
+ "preview.back": "Back to the entry",
100
+ "preview.pane": "Preview",
101
+ "preview.entry.title": "Entry",
102
+ "preview.entry.readOnly": "Read-only",
103
+ "preview.entry.body": "The form is in the Content Manager: open it in another tab, edit and save the entry there. This page shows the saved draft and loads it again after each save.",
104
+ "preview.entry.bodyReadOnly": "You may read this entry but not edit it. The preview shows its saved draft.",
105
+ "preview.entry.locale": "Locale: {locale}",
106
+ "preview.entry.saved": "Draft saved {date}",
107
+ "preview.entry.open": "Open in the Content Manager, in a new tab",
108
+ "preview.entry.view": "View in the Content Manager, in a new tab",
109
+ "preview.device.group": "Preview width",
110
+ "preview.device.phone": "Phone",
111
+ "preview.device.tablet": "Tablet",
112
+ "preview.device.desktop": "Desktop",
113
+ "preview.static": "As static site",
114
+ "preview.locale": "Locale",
115
+ "preview.reload": "Reload",
116
+ "preview.updating": "Updating the preview",
117
+ "preview.shortcuts": "Shortcuts: Ctrl+Shift+1, 2 and 3 for phone, tablet and desktop; Ctrl+Shift+S for the static site; Ctrl+Shift+Left Arrow back to the entry. On a Mac use Command instead of Ctrl. They do not work while you type in a field.",
118
+ "preview.frame.title": "Preview of {title}",
119
+ "preview.blocked.title": "The admin does not allow this site in a frame",
120
+ "preview.blocked.body": "The Content Security Policy of the admin does not list {origin} in frame-src, so the browser refuses to show it. Add it to frame-src in the strapi::security config of the Strapi project and restart it. The host check on the Micelio status page reports it too.",
121
+ "preview.upgrade.title": "Upgrade the frontend",
122
+ "preview.upgrade.body": "The site's frontend speaks the preview protocol versions {versions}, and this plugin speaks none of them. The preview stays plain: it shows the draft and loads it again as a whole after each save.",
123
+ "preview.error.entry": "The entry could not be loaded.",
124
+ "preview.error.missing": "This entry does not exist.",
125
+ "preview.error.unavailable": "This site has no preview: set its url in the Content Manager (Site), and make sure the frontend runs there.",
126
+ "preview.error.forbidden": "You may not preview this entry. It needs the permission to preview drafts and the permission to read the entry.",
127
+ "preview.error.unsupported": "This type has no preview: its entries do not belong to a site.",
128
+ "preview.error.busy": "Too many preview requests. Wait a minute, then reload.",
129
+ "preview.error.failed": "The preview could not load. Reload to try again.",
130
+ "action.preview": "Preview",
131
+ "action.preview.unsaved": "Preview: save your changes first"
96
132
  };
97
133
  exports.default = en;
@@ -90,7 +90,43 @@ const en = {
90
90
  "status.plugins.fix": "Fix",
91
91
  "status.diagnostics.copy": "Copy diagnostics",
92
92
  "status.diagnostics.copied": "The diagnostics were copied.",
93
- "status.diagnostics.failed": "The diagnostics could not be copied."
93
+ "status.diagnostics.failed": "The diagnostics could not be copied.",
94
+ "preview.title": "Preview",
95
+ "preview.page": "Preview of {title}",
96
+ "preview.subtitle": "The saved draft, as the site renders it.",
97
+ "preview.back": "Back to the entry",
98
+ "preview.pane": "Preview",
99
+ "preview.entry.title": "Entry",
100
+ "preview.entry.readOnly": "Read-only",
101
+ "preview.entry.body": "The form is in the Content Manager: open it in another tab, edit and save the entry there. This page shows the saved draft and loads it again after each save.",
102
+ "preview.entry.bodyReadOnly": "You may read this entry but not edit it. The preview shows its saved draft.",
103
+ "preview.entry.locale": "Locale: {locale}",
104
+ "preview.entry.saved": "Draft saved {date}",
105
+ "preview.entry.open": "Open in the Content Manager, in a new tab",
106
+ "preview.entry.view": "View in the Content Manager, in a new tab",
107
+ "preview.device.group": "Preview width",
108
+ "preview.device.phone": "Phone",
109
+ "preview.device.tablet": "Tablet",
110
+ "preview.device.desktop": "Desktop",
111
+ "preview.static": "As static site",
112
+ "preview.locale": "Locale",
113
+ "preview.reload": "Reload",
114
+ "preview.updating": "Updating the preview",
115
+ "preview.shortcuts": "Shortcuts: Ctrl+Shift+1, 2 and 3 for phone, tablet and desktop; Ctrl+Shift+S for the static site; Ctrl+Shift+Left Arrow back to the entry. On a Mac use Command instead of Ctrl. They do not work while you type in a field.",
116
+ "preview.frame.title": "Preview of {title}",
117
+ "preview.blocked.title": "The admin does not allow this site in a frame",
118
+ "preview.blocked.body": "The Content Security Policy of the admin does not list {origin} in frame-src, so the browser refuses to show it. Add it to frame-src in the strapi::security config of the Strapi project and restart it. The host check on the Micelio status page reports it too.",
119
+ "preview.upgrade.title": "Upgrade the frontend",
120
+ "preview.upgrade.body": "The site's frontend speaks the preview protocol versions {versions}, and this plugin speaks none of them. The preview stays plain: it shows the draft and loads it again as a whole after each save.",
121
+ "preview.error.entry": "The entry could not be loaded.",
122
+ "preview.error.missing": "This entry does not exist.",
123
+ "preview.error.unavailable": "This site has no preview: set its url in the Content Manager (Site), and make sure the frontend runs there.",
124
+ "preview.error.forbidden": "You may not preview this entry. It needs the permission to preview drafts and the permission to read the entry.",
125
+ "preview.error.unsupported": "This type has no preview: its entries do not belong to a site.",
126
+ "preview.error.busy": "Too many preview requests. Wait a minute, then reload.",
127
+ "preview.error.failed": "The preview could not load. Reload to try again.",
128
+ "action.preview": "Preview",
129
+ "action.preview.unsaved": "Preview: save your changes first"
94
130
  };
95
131
  export {
96
132
  en as default
@@ -92,6 +92,42 @@ const es = {
92
92
  "status.plugins.fix": "Solución",
93
93
  "status.diagnostics.copy": "Copiar diagnóstico",
94
94
  "status.diagnostics.copied": "Se copió el diagnóstico.",
95
- "status.diagnostics.failed": "No se pudo copiar el diagnóstico."
95
+ "status.diagnostics.failed": "No se pudo copiar el diagnóstico.",
96
+ "preview.title": "Vista previa",
97
+ "preview.page": "Vista previa de {title}",
98
+ "preview.subtitle": "El borrador guardado, tal como lo muestra el sitio.",
99
+ "preview.back": "Volver a la entrada",
100
+ "preview.pane": "Vista previa",
101
+ "preview.entry.title": "Entrada",
102
+ "preview.entry.readOnly": "Solo lectura",
103
+ "preview.entry.body": "El formulario está en el Gestor de contenido: ábrelo en otra pestaña, edita y guarda la entrada allí. Esta página muestra el borrador guardado y lo carga de nuevo tras cada guardado.",
104
+ "preview.entry.bodyReadOnly": "Puedes leer esta entrada pero no editarla. La vista previa muestra su borrador guardado.",
105
+ "preview.entry.locale": "Idioma: {locale}",
106
+ "preview.entry.saved": "Borrador guardado {date}",
107
+ "preview.entry.open": "Abrir en el Gestor de contenido, en una pestaña nueva",
108
+ "preview.entry.view": "Ver en el Gestor de contenido, en una pestaña nueva",
109
+ "preview.device.group": "Ancho de la vista previa",
110
+ "preview.device.phone": "Móvil",
111
+ "preview.device.tablet": "Tableta",
112
+ "preview.device.desktop": "Escritorio",
113
+ "preview.static": "Como sitio estático",
114
+ "preview.locale": "Idioma",
115
+ "preview.reload": "Recargar",
116
+ "preview.updating": "Actualizando la vista previa",
117
+ "preview.shortcuts": "Atajos: Ctrl+Mayús+1, 2 y 3 para móvil, tableta y escritorio; Ctrl+Mayús+S para el sitio estático; Ctrl+Mayús+Flecha izquierda para volver a la entrada. En Mac usa Comando en lugar de Ctrl. No funcionan mientras escribes en un campo.",
118
+ "preview.frame.title": "Vista previa de {title}",
119
+ "preview.blocked.title": "El panel no permite este sitio en un marco",
120
+ "preview.blocked.body": "La política de seguridad de contenido del panel no incluye {origin} en frame-src, así que el navegador se niega a mostrarlo. Añádelo a frame-src en la configuración de strapi::security del proyecto Strapi y reinícialo. La comprobación del host en la página de estado de Micelio también lo indica.",
121
+ "preview.upgrade.title": "Actualiza el frontend",
122
+ "preview.upgrade.body": "El frontend del sitio habla las versiones {versions} del protocolo de vista previa y este plugin no habla ninguna. La vista previa sigue siendo simple: muestra el borrador y lo carga de nuevo entero tras cada guardado.",
123
+ "preview.error.entry": "No se pudo cargar la entrada.",
124
+ "preview.error.missing": "Esta entrada no existe.",
125
+ "preview.error.unavailable": "Este sitio no tiene vista previa: define su url en el Gestor de contenido (Sitio) y comprueba que el frontend funciona allí.",
126
+ "preview.error.forbidden": "No puedes previsualizar esta entrada. Hace falta el permiso para previsualizar borradores y el permiso para leer la entrada.",
127
+ "preview.error.unsupported": "Este tipo no tiene vista previa: sus entradas no pertenecen a un sitio.",
128
+ "preview.error.busy": "Demasiadas solicitudes de vista previa. Espera un minuto y recarga.",
129
+ "preview.error.failed": "No se pudo cargar la vista previa. Recarga para intentarlo de nuevo.",
130
+ "action.preview": "Vista previa",
131
+ "action.preview.unsaved": "Vista previa: guarda primero los cambios"
96
132
  };
97
133
  exports.default = es;
@@ -90,7 +90,43 @@ const es = {
90
90
  "status.plugins.fix": "Solución",
91
91
  "status.diagnostics.copy": "Copiar diagnóstico",
92
92
  "status.diagnostics.copied": "Se copió el diagnóstico.",
93
- "status.diagnostics.failed": "No se pudo copiar el diagnóstico."
93
+ "status.diagnostics.failed": "No se pudo copiar el diagnóstico.",
94
+ "preview.title": "Vista previa",
95
+ "preview.page": "Vista previa de {title}",
96
+ "preview.subtitle": "El borrador guardado, tal como lo muestra el sitio.",
97
+ "preview.back": "Volver a la entrada",
98
+ "preview.pane": "Vista previa",
99
+ "preview.entry.title": "Entrada",
100
+ "preview.entry.readOnly": "Solo lectura",
101
+ "preview.entry.body": "El formulario está en el Gestor de contenido: ábrelo en otra pestaña, edita y guarda la entrada allí. Esta página muestra el borrador guardado y lo carga de nuevo tras cada guardado.",
102
+ "preview.entry.bodyReadOnly": "Puedes leer esta entrada pero no editarla. La vista previa muestra su borrador guardado.",
103
+ "preview.entry.locale": "Idioma: {locale}",
104
+ "preview.entry.saved": "Borrador guardado {date}",
105
+ "preview.entry.open": "Abrir en el Gestor de contenido, en una pestaña nueva",
106
+ "preview.entry.view": "Ver en el Gestor de contenido, en una pestaña nueva",
107
+ "preview.device.group": "Ancho de la vista previa",
108
+ "preview.device.phone": "Móvil",
109
+ "preview.device.tablet": "Tableta",
110
+ "preview.device.desktop": "Escritorio",
111
+ "preview.static": "Como sitio estático",
112
+ "preview.locale": "Idioma",
113
+ "preview.reload": "Recargar",
114
+ "preview.updating": "Actualizando la vista previa",
115
+ "preview.shortcuts": "Atajos: Ctrl+Mayús+1, 2 y 3 para móvil, tableta y escritorio; Ctrl+Mayús+S para el sitio estático; Ctrl+Mayús+Flecha izquierda para volver a la entrada. En Mac usa Comando en lugar de Ctrl. No funcionan mientras escribes en un campo.",
116
+ "preview.frame.title": "Vista previa de {title}",
117
+ "preview.blocked.title": "El panel no permite este sitio en un marco",
118
+ "preview.blocked.body": "La política de seguridad de contenido del panel no incluye {origin} en frame-src, así que el navegador se niega a mostrarlo. Añádelo a frame-src en la configuración de strapi::security del proyecto Strapi y reinícialo. La comprobación del host en la página de estado de Micelio también lo indica.",
119
+ "preview.upgrade.title": "Actualiza el frontend",
120
+ "preview.upgrade.body": "El frontend del sitio habla las versiones {versions} del protocolo de vista previa y este plugin no habla ninguna. La vista previa sigue siendo simple: muestra el borrador y lo carga de nuevo entero tras cada guardado.",
121
+ "preview.error.entry": "No se pudo cargar la entrada.",
122
+ "preview.error.missing": "Esta entrada no existe.",
123
+ "preview.error.unavailable": "Este sitio no tiene vista previa: define su url en el Gestor de contenido (Sitio) y comprueba que el frontend funciona allí.",
124
+ "preview.error.forbidden": "No puedes previsualizar esta entrada. Hace falta el permiso para previsualizar borradores y el permiso para leer la entrada.",
125
+ "preview.error.unsupported": "Este tipo no tiene vista previa: sus entradas no pertenecen a un sitio.",
126
+ "preview.error.busy": "Demasiadas solicitudes de vista previa. Espera un minuto y recarga.",
127
+ "preview.error.failed": "No se pudo cargar la vista previa. Recarga para intentarlo de nuevo.",
128
+ "action.preview": "Vista previa",
129
+ "action.preview.unsaved": "Vista previa: guarda primero los cambios"
94
130
  };
95
131
  export {
96
132
  es as default
@@ -72,7 +72,7 @@ const plugin = {
72
72
  icon: MicelioIcon,
73
73
  intlLabel: { id: getTranslation("plugin.name"), defaultMessage: "Micelio" },
74
74
  permissions: [],
75
- Component: () => Promise.resolve().then(() => require("./MicelioLayout-DXzRS4l3.js"))
75
+ Component: () => Promise.resolve().then(() => require("./MicelioLayout-Bmu5xCw9.js"))
76
76
  });
77
77
  app.registerPlugin({
78
78
  id: PLUGIN_ID,
@@ -83,11 +83,17 @@ const plugin = {
83
83
  apis: { registerPage }
84
84
  });
85
85
  },
86
+ async bootstrap(app) {
87
+ const add = app.getPlugin("content-manager").apis.addDocumentHeaderAction;
88
+ if (typeof add !== "function") return;
89
+ const { PreviewHeaderAction } = await Promise.resolve().then(() => require("./PreviewHeaderAction-CGHycv4I.js"));
90
+ add([PreviewHeaderAction]);
91
+ },
86
92
  async registerTrads({ locales }) {
87
93
  return Promise.all(
88
94
  locales.map(async (locale) => {
89
95
  try {
90
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-D-T8z3qh.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-RV0vkOHh.js")) }), `./translations/${locale}.json`, 3);
96
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-BDVuq0fN.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-DStBGkm7.js")) }), `./translations/${locale}.json`, 3);
91
97
  const prefixed = {};
92
98
  for (const [key, value] of Object.entries(data)) {
93
99
  prefixed[getTranslation(key)] = value;
@@ -71,7 +71,7 @@ const plugin = {
71
71
  icon: MicelioIcon,
72
72
  intlLabel: { id: getTranslation("plugin.name"), defaultMessage: "Micelio" },
73
73
  permissions: [],
74
- Component: () => import("./MicelioLayout-3sKGPS_0.mjs")
74
+ Component: () => import("./MicelioLayout-DMMsKnMo.mjs")
75
75
  });
76
76
  app.registerPlugin({
77
77
  id: PLUGIN_ID,
@@ -82,11 +82,17 @@ const plugin = {
82
82
  apis: { registerPage }
83
83
  });
84
84
  },
85
+ async bootstrap(app) {
86
+ const add = app.getPlugin("content-manager").apis.addDocumentHeaderAction;
87
+ if (typeof add !== "function") return;
88
+ const { PreviewHeaderAction } = await import("./PreviewHeaderAction-BaduMvYy.mjs");
89
+ add([PreviewHeaderAction]);
90
+ },
85
91
  async registerTrads({ locales }) {
86
92
  return Promise.all(
87
93
  locales.map(async (locale) => {
88
94
  try {
89
- const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-C2O-S2ym.mjs"), "./translations/es.json": () => import("./es-CQ2tv9N8.mjs") }), `./translations/${locale}.json`, 3);
95
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-Bahc6y6N.mjs"), "./translations/es.json": () => import("./es-nUHBIoZG.mjs") }), `./translations/${locale}.json`, 3);
90
96
  const prefixed = {};
91
97
  for (const [key, value] of Object.entries(data)) {
92
98
  prefixed[getTranslation(key)] = value;
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const index = require("./index-D3TGbaQ7.js");
3
+ const index = require("./index-D_Dbvo2F.js");
4
4
  exports.default = index.plugin;
@@ -1,4 +1,4 @@
1
- import { a } from "./index-CiSujTES.mjs";
1
+ import { a } from "./index-Dwr15ZsB.mjs";
2
2
  export {
3
3
  a as default
4
4
  };
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ const reactIntl = require("react-intl");
3
+ const en = require("./en-BDVuq0fN.js");
4
+ const index = require("./index-D_Dbvo2F.js");
5
+ function hasMessage(id) {
6
+ return Object.hasOwn(en.default, id);
7
+ }
8
+ function useMessages() {
9
+ const { formatMessage, formatDate } = reactIntl.useIntl();
10
+ return {
11
+ t: (id, values) => formatMessage(
12
+ { id: index.getTranslation(id), defaultMessage: en.default[id] ?? id },
13
+ values
14
+ ),
15
+ // Seconds included: a run again within the same minute must read as new.
16
+ date: (iso) => formatDate(iso, { dateStyle: "medium", timeStyle: "medium" })
17
+ };
18
+ }
19
+ const STATUS_READ_PERMISSION = { action: "plugin::micelio.status.read", subject: null };
20
+ const PREVIEW_PERMISSION = { action: "plugin::micelio.preview", subject: null };
21
+ exports.PREVIEW_PERMISSION = PREVIEW_PERMISSION;
22
+ exports.STATUS_READ_PERMISSION = STATUS_READ_PERMISSION;
23
+ exports.hasMessage = hasMessage;
24
+ exports.useMessages = useMessages;
@@ -0,0 +1,25 @@
1
+ import { useIntl } from "react-intl";
2
+ import en from "./en-Bahc6y6N.mjs";
3
+ import { g as getTranslation } from "./index-Dwr15ZsB.mjs";
4
+ function hasMessage(id) {
5
+ return Object.hasOwn(en, id);
6
+ }
7
+ function useMessages() {
8
+ const { formatMessage, formatDate } = useIntl();
9
+ return {
10
+ t: (id, values) => formatMessage(
11
+ { id: getTranslation(id), defaultMessage: en[id] ?? id },
12
+ values
13
+ ),
14
+ // Seconds included: a run again within the same minute must read as new.
15
+ date: (iso) => formatDate(iso, { dateStyle: "medium", timeStyle: "medium" })
16
+ };
17
+ }
18
+ const STATUS_READ_PERMISSION = { action: "plugin::micelio.status.read", subject: null };
19
+ const PREVIEW_PERMISSION = { action: "plugin::micelio.preview", subject: null };
20
+ export {
21
+ PREVIEW_PERMISSION as P,
22
+ STATUS_READ_PERMISSION as S,
23
+ hasMessage as h,
24
+ useMessages as u
25
+ };
@@ -0,0 +1,8 @@
1
+ import { SVGProps } from 'react';
2
+ /**
3
+ * The icon of the preview action: an eye.
4
+ *
5
+ * @remarks
6
+ * Drawn here for the reason `MicelioIcon` is: `@strapi/icons` cannot be required in Node.
7
+ */
8
+ export declare const EyeIcon: (props: SVGProps<SVGSVGElement>) => import("react").JSX.Element;
@@ -0,0 +1,47 @@
1
+ /** Whether the frame said it speaks a version the admin speaks. */
2
+ export type FrameProtocol = 'waiting' | 'ok' | 'upgrade';
3
+ /** What the frame reports to the page. */
4
+ export interface FrameState {
5
+ /** What the frame in view said with `ready`. */
6
+ protocol: FrameProtocol;
7
+ /** The versions the frame listed in its `ready`, once it sent one. */
8
+ versions: number[];
9
+ /** A frame is loading, or the one in view is loading the draft again after a `load-draft`. */
10
+ isBusy: boolean;
11
+ }
12
+ /** What the page can ask of the frame. */
13
+ export interface PreviewFrameHandle {
14
+ /**
15
+ * Tells the frame in view to load the draft again, with `load-draft` to the site's origin only.
16
+ *
17
+ * @returns `false` when the frame has not said `ready` with a version the admin speaks: nothing was
18
+ * sent, and the page loads the preview again with a new token.
19
+ */
20
+ loadDraft: () => boolean;
21
+ }
22
+ interface PreviewFrameProps {
23
+ /** The URL to load; a new value loads it in a frame of its own, and the old render stays until it is ready. */
24
+ src: string | undefined;
25
+ /** The site's origin: the only one the page posts to and reads messages from. */
26
+ origin: string | undefined;
27
+ documentId: string;
28
+ /** `null` for a type that is not localised. */
29
+ locale: string | null;
30
+ title: string;
31
+ /** The width of the page in the frame, in pixels; the frame never grows past its column. */
32
+ width: number;
33
+ onChange: (state: FrameState) => void;
34
+ /** The admin's Content Security Policy refused to frame `origin`. */
35
+ onBlocked: () => void;
36
+ }
37
+ /**
38
+ * The preview iframe of the preview page (ADR-0019, sections 4 and 5): loads the URL the token route
39
+ * answered, listens for `ready` from the site's origin only and posts `load-draft` to it.
40
+ *
41
+ * @remarks
42
+ * Messages from another origin or window, or that fail the schema of `contract/preview.ts`, are dropped.
43
+ * The iframe is sandboxed as the ADR says. A new `src` loads in a second frame stacked on the first,
44
+ * which the page shows once it has loaded.
45
+ */
46
+ export declare const PreviewFrame: import('react').ForwardRefExoticComponent<PreviewFrameProps & import('react').RefAttributes<PreviewFrameHandle>>;
47
+ export {};
@@ -0,0 +1,28 @@
1
+ /** What Strapi hands a header action of the edit view; the fields this action reads. */
2
+ interface HeaderActionProps {
3
+ collectionType: string;
4
+ /** `undefined` while the entry is being created. */
5
+ documentId?: string;
6
+ document?: {
7
+ locale?: string | null;
8
+ };
9
+ }
10
+ /** The description Strapi renders as a button of the edit view's header. */
11
+ interface HeaderActionDescription {
12
+ label: string;
13
+ icon: JSX.Element;
14
+ disabled: boolean;
15
+ onClick: () => void;
16
+ }
17
+ /**
18
+ * The **Preview** button in the header of the edit view (issue #47): it opens the preview page of the
19
+ * entry. Strapi runs it as a hook, once for each edit view, and shows what it returns.
20
+ *
21
+ * @remarks
22
+ * It shows only for a saved entry of a collection type that has a preview (`hasPreview`) to a role
23
+ * that may preview. While the form has unsaved changes it is disabled, and its label, which is also
24
+ * its tooltip and its accessible name, says to save first: the preview shows the saved draft.
25
+ * The name of the function is what Strapi keys the button by.
26
+ */
27
+ export declare function PreviewHeaderAction({ collectionType, documentId, document, }: HeaderActionProps): HeaderActionDescription | null;
28
+ export {};
@@ -0,0 +1,31 @@
1
+ /** The entry the preview shows, as the Content Manager API answers it. */
2
+ export interface Entry {
3
+ title: string;
4
+ /** `null` for a type that is not localised. */
5
+ locale: string | null;
6
+ /** The locales the entry has a version in. */
7
+ locales: string[];
8
+ /** When the draft was last saved, as an ISO 8601 date. */
9
+ updatedAt: string;
10
+ }
11
+ /** What the page knows of the entry. */
12
+ export type EntryResource = {
13
+ kind: 'loading';
14
+ } | {
15
+ kind: 'ready';
16
+ entry: Entry;
17
+ } | {
18
+ kind: 'error';
19
+ status: number | undefined;
20
+ };
21
+ /**
22
+ * Reads the entry from the Content Manager's API and reads it again every few seconds while the
23
+ * page is in view, and when it comes back into view, to notice a save made in the form.
24
+ *
25
+ * @returns The entry, and `savedCount`, which goes up by one each time the draft's `updatedAt`
26
+ * changes after the first read. A change of locale starts counting again.
27
+ */
28
+ export declare function useEntry(uid: string, documentId: string, locale: string | null): {
29
+ resource: EntryResource;
30
+ savedCount: number;
31
+ };
@@ -0,0 +1,31 @@
1
+ import { PreviewMode } from '../utils/previewPane';
2
+ /** Why there is no URL to load. */
3
+ export type PreviewSourceError = 'unavailable' | 'forbidden' | 'missing' | 'unsupported' | 'busy' | 'failed';
4
+ /** The URL of the iframe, or why there is none. */
5
+ export type PreviewSource = {
6
+ kind: 'loading';
7
+ } | {
8
+ kind: 'ready';
9
+ src: string;
10
+ origin: string;
11
+ } | {
12
+ kind: 'error';
13
+ reason: PreviewSourceError;
14
+ };
15
+ /**
16
+ * Asks `POST /micelio/preview/token` for the URL of the iframe (ADR-0019, section 3), and again when
17
+ * `reload()` is called, when the entry, locale or mode change, and before the grant expires.
18
+ *
19
+ * @remarks
20
+ * The token is single-use and 60 seconds old at most, so it is requested only when a frame is about
21
+ * to load. The previous source stays in `source` while the next one is requested, so the old render
22
+ * stays in view.
23
+ */
24
+ export declare function usePreviewSource(entry: {
25
+ uid: string;
26
+ documentId: string;
27
+ locale: string | null;
28
+ } | undefined, mode: PreviewMode): {
29
+ source: PreviewSource;
30
+ reload: () => void;
31
+ };
@@ -5,6 +5,7 @@
5
5
  * The core's items (`navigation.ts`) show to a role that has their permission; the pages of
6
6
  * sibling plugins come from `GET /micelio/admin-pages`, which lists only those the admin user has
7
7
  * the permission for. A group with nothing to show is left out. A role without the Status
8
- * permission lands on About.
8
+ * permission lands on About. The preview page (`preview/:uid/:documentId`) has no item: the entry's edit view
9
+ * opens it, and its own permission protects it.
9
10
  */
10
11
  export default function MicelioLayout(): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ /** The preview of one entry beside the link to its form (issue #45); opened from the entry's edit view. */
2
+ export default function PreviewPage(): import("react").JSX.Element;
@@ -3,3 +3,8 @@ export declare const STATUS_READ_PERMISSION: {
3
3
  action: string;
4
4
  subject: null;
5
5
  };
6
+ /** Opens the preview page; the server's `PREVIEW_PERMISSION`, registered in its `bootstrap`. */
7
+ export declare const PREVIEW_PERMISSION: {
8
+ action: string;
9
+ subject: null;
10
+ };