@pantheon-systems/p1-media 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @pantheon-systems/p1-media
2
+
3
+ Media library plugin for the P1 Puck editor, backed by Cloudflare R2. Adds asset upload and
4
+ browsing to the editor, plus render helpers and a ready-made media figure block.
5
+
6
+ > Part of Pantheon's **P1** platform. It is published publicly so P1 applications can install
7
+ > it, but it requires the Pantheon-hosted media Worker and is not a general-purpose Puck
8
+ > plugin. Pre-1.0: minor versions may carry breaking changes.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @pantheon-systems/p1-media
14
+ ```
15
+
16
+ Peer dependencies:
17
+
18
+ ```bash
19
+ npm install @pantheon-systems/puck-css @puckeditor/core react
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Add the plugin to the editor:
25
+
26
+ ```tsx
27
+ import { createMediaPlugin } from "@pantheon-systems/p1-media";
28
+
29
+ const mediaPlugin = createMediaPlugin({
30
+ workerUrl: process.env.NEXT_PUBLIC_MEDIA_WORKER_URL,
31
+ });
32
+ ```
33
+
34
+ `siteId` defaults to the ambient `P1PuckProvider` site context — pass it explicitly only when
35
+ rendering outside that provider, or to override it. `workerUrl` defaults to the production
36
+ media host.
37
+
38
+ Render uploaded media with the provided components, which handle R2 image transformation URLs:
39
+
40
+ ```tsx
41
+ import { MediaImage, MediaFigure, buildImageUrl } from "@pantheon-systems/p1-media";
42
+ ```
43
+
44
+ `createMediaFigureBlock` builds a drop-in Puck block for captioned media.
45
+
46
+ ## Entry points
47
+
48
+ | Import | Contents |
49
+ | --- | --- |
50
+ | `@pantheon-systems/p1-media` | Plugin, render components, media value helpers |
51
+ | `.../server` | Server-side helpers |
52
+
53
+ ## Note on the package name
54
+
55
+ The source directory is `packages/p1-media-r2/`, but the package publishes as
56
+ `@pantheon-systems/p1-media`.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { ReactElement, ImgHTMLAttributes, ReactNode } from 'react';
3
3
  import { ComponentConfig } from '@puckeditor/core';
4
4
 
@@ -116,8 +116,8 @@ declare function createMediaPlugin(options: MediaPluginOptions): {
116
116
  name: string;
117
117
  overrides: {
118
118
  fieldTypes: {
119
- text: (props: any) => react_jsx_runtime.JSX.Element;
120
- "p1-media": (props: any) => react_jsx_runtime.JSX.Element;
119
+ text: (props: any) => react.JSX.Element;
120
+ "p1-media": (props: any) => react.JSX.Element;
121
121
  };
122
122
  };
123
123
  fieldTransforms: {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { ReactElement, ImgHTMLAttributes, ReactNode } from 'react';
3
3
  import { ComponentConfig } from '@puckeditor/core';
4
4
 
@@ -116,8 +116,8 @@ declare function createMediaPlugin(options: MediaPluginOptions): {
116
116
  name: string;
117
117
  overrides: {
118
118
  fieldTypes: {
119
- text: (props: any) => react_jsx_runtime.JSX.Element;
120
- "p1-media": (props: any) => react_jsx_runtime.JSX.Element;
119
+ text: (props: any) => react.JSX.Element;
120
+ "p1-media": (props: any) => react.JSX.Element;
121
121
  };
122
122
  };
123
123
  fieldTransforms: {
package/dist/index.js CHANGED
@@ -59,6 +59,46 @@ var DEFAULT_MEDIA_PATTERNS = [
59
59
  // src/components/media-field.tsx
60
60
  var import_react7 = require("react");
61
61
 
62
+ // src/crop.ts
63
+ function getBaseUrl(value) {
64
+ return value ? value.split("?")[0] : "";
65
+ }
66
+ function getParams(value) {
67
+ return new URLSearchParams(value.includes("?") ? value.split("?")[1] : "");
68
+ }
69
+ function getCropMode(value) {
70
+ if (!value) return "fit";
71
+ const params = getParams(value);
72
+ if (params.has("trim.left")) return "custom";
73
+ return params.get("fit") === "cover" ? "smart" : "fit";
74
+ }
75
+ function getTrimRect(value) {
76
+ if (!value) return null;
77
+ const params = getParams(value);
78
+ const read = (key) => {
79
+ const raw = params.get(key);
80
+ if (raw === null || raw === "") return null;
81
+ const n2 = Number(raw);
82
+ return Number.isFinite(n2) ? n2 : null;
83
+ };
84
+ const left = read("trim.left");
85
+ const top = read("trim.top");
86
+ const width = read("trim.width");
87
+ const height = read("trim.height");
88
+ if (left === null || top === null || width === null || height === null) return null;
89
+ return { left, top, width, height };
90
+ }
91
+ function buildValueWithCrop(baseUrl, crop) {
92
+ return crop === "smart" ? `${baseUrl}?fit=cover&gravity=auto` : `${baseUrl}?fit=scale-down`;
93
+ }
94
+ function buildValueWithTrim(baseUrl, rect) {
95
+ const left = Math.max(0, Math.round(rect.left));
96
+ const top = Math.max(0, Math.round(rect.top));
97
+ const width = Math.max(1, Math.round(rect.width));
98
+ const height = Math.max(1, Math.round(rect.height));
99
+ return `${baseUrl}?trim.left=${left}&trim.top=${top}&trim.width=${width}&trim.height=${height}`;
100
+ }
101
+
62
102
  // src/components/media-library.tsx
63
103
  var import_react6 = require("react");
64
104
 
@@ -224,6 +264,9 @@ async function runUpload(target, file, metadata, assetId, progress, onStep) {
224
264
  uploaded = true;
225
265
  }
226
266
  const nextProgress = { presigned, uploaded };
267
+ if (presigned === void 0) {
268
+ throw new UploadFlowError("Resume state is missing its presign result", nextProgress);
269
+ }
227
270
  const presignedResult = presigned;
228
271
  onStep("finalizing");
229
272
  const finalizePath = assetId ? `/media/${encodeURIComponent(assetId)}/versions/finalize` : "/media/finalize";
@@ -1326,12 +1369,13 @@ function MediaLibrary({ isOpen, onClose, onSelect, onSelectItem }) {
1326
1369
  onClose();
1327
1370
  };
1328
1371
  const deleteItem = (item) => {
1329
- if (!item.assetId) return;
1372
+ const { assetId } = item;
1373
+ if (!assetId) return;
1330
1374
  (async () => {
1331
1375
  try {
1332
1376
  const params = siteParams(config);
1333
1377
  const response = await fetch(
1334
- config.workerUrl + "/media/" + encodeURIComponent(item.assetId) + "?" + params.toString(),
1378
+ config.workerUrl + "/media/" + encodeURIComponent(assetId) + "?" + params.toString(),
1335
1379
  {
1336
1380
  method: "DELETE",
1337
1381
  headers: await getAuthHeaders()
@@ -1469,46 +1513,6 @@ function MediaLibrary({ isOpen, onClose, onSelect, onSelectItem }) {
1469
1513
  );
1470
1514
  }
1471
1515
 
1472
- // src/crop.ts
1473
- function getBaseUrl(value) {
1474
- return value ? value.split("?")[0] : "";
1475
- }
1476
- function getParams(value) {
1477
- return new URLSearchParams(value.includes("?") ? value.split("?")[1] : "");
1478
- }
1479
- function getCropMode(value) {
1480
- if (!value) return "fit";
1481
- const params = getParams(value);
1482
- if (params.has("trim.left")) return "custom";
1483
- return params.get("fit") === "cover" ? "smart" : "fit";
1484
- }
1485
- function getTrimRect(value) {
1486
- if (!value) return null;
1487
- const params = getParams(value);
1488
- const read = (key) => {
1489
- const raw = params.get(key);
1490
- if (raw === null || raw === "") return null;
1491
- const n2 = Number(raw);
1492
- return Number.isFinite(n2) ? n2 : null;
1493
- };
1494
- const left = read("trim.left");
1495
- const top = read("trim.top");
1496
- const width = read("trim.width");
1497
- const height = read("trim.height");
1498
- if (left === null || top === null || width === null || height === null) return null;
1499
- return { left, top, width, height };
1500
- }
1501
- function buildValueWithCrop(baseUrl, crop) {
1502
- return crop === "smart" ? `${baseUrl}?fit=cover&gravity=auto` : `${baseUrl}?fit=scale-down`;
1503
- }
1504
- function buildValueWithTrim(baseUrl, rect) {
1505
- const left = Math.max(0, Math.round(rect.left));
1506
- const top = Math.max(0, Math.round(rect.top));
1507
- const width = Math.max(1, Math.round(rect.width));
1508
- const height = Math.max(1, Math.round(rect.height));
1509
- return `${baseUrl}?trim.left=${left}&trim.top=${top}&trim.width=${width}&trim.height=${height}`;
1510
- }
1511
-
1512
1516
  // src/components/media-field.tsx
1513
1517
  var import_jsx_runtime8 = require("react/jsx-runtime");
1514
1518
  function MediaFieldRender(props) {
@@ -1663,10 +1667,73 @@ function MediaFieldRender(props) {
1663
1667
  // src/components/media-object-field.tsx
1664
1668
  var import_react10 = require("react");
1665
1669
 
1670
+ // src/media-value.ts
1671
+ var STRUCTURAL_MEDIA_KEYS = /* @__PURE__ */ new Set([
1672
+ "assetId",
1673
+ "versionId",
1674
+ "url",
1675
+ "metaSchemaVersion"
1676
+ ]);
1677
+ function isMediaValue(value) {
1678
+ return typeof value === "object" && value !== null;
1679
+ }
1680
+ function makeMediaValue(input) {
1681
+ if (!input.assetId || !input.versionId) {
1682
+ return input.url;
1683
+ }
1684
+ const value = {
1685
+ assetId: input.assetId,
1686
+ versionId: input.versionId,
1687
+ url: input.url
1688
+ };
1689
+ if (typeof input.metaSchemaVersion === "number") {
1690
+ value.metaSchemaVersion = input.metaSchemaVersion;
1691
+ }
1692
+ if (input.metadata) {
1693
+ for (const [k, v] of Object.entries(input.metadata)) {
1694
+ if (v === void 0 || v === "") continue;
1695
+ if (STRUCTURAL_MEDIA_KEYS.has(k)) continue;
1696
+ value[k] = v;
1697
+ }
1698
+ }
1699
+ return value;
1700
+ }
1701
+ function buildValueFromAsset(asset, crop) {
1702
+ const metadata = { ...asset.metadata ?? {} };
1703
+ if (typeof asset.width === "number") metadata.width = asset.width;
1704
+ if (typeof asset.height === "number") metadata.height = asset.height;
1705
+ return makeMediaValue({
1706
+ assetId: asset.assetId,
1707
+ versionId: asset.versionId,
1708
+ url: buildValueWithCrop(getBaseUrl(asset.url), crop),
1709
+ metaSchemaVersion: asset.metaSchemaVersion,
1710
+ metadata
1711
+ });
1712
+ }
1713
+ function applyCropToValue(value, crop) {
1714
+ const currentUrl = isMediaValue(value) ? value.url : value;
1715
+ const baseUrl = getBaseUrl(typeof currentUrl === "string" ? currentUrl : "");
1716
+ if (!baseUrl) return value;
1717
+ const url = buildValueWithCrop(baseUrl, crop);
1718
+ return isMediaValue(value) ? { ...value, url } : url;
1719
+ }
1720
+ function applyTrimToValue(value, rect) {
1721
+ const currentUrl = isMediaValue(value) ? value.url : value;
1722
+ const baseUrl = getBaseUrl(typeof currentUrl === "string" ? currentUrl : "");
1723
+ if (!baseUrl) return value;
1724
+ const url = buildValueWithTrim(baseUrl, rect);
1725
+ return isMediaValue(value) ? { ...value, url } : url;
1726
+ }
1727
+ function setMetaOnValue(value, fieldName, fieldValue) {
1728
+ if (!isMediaValue(value)) return value;
1729
+ if (STRUCTURAL_MEDIA_KEYS.has(fieldName)) return value;
1730
+ return { ...value, [fieldName]: fieldValue };
1731
+ }
1732
+
1666
1733
  // src/components/crop-dialog.tsx
1667
1734
  var import_react9 = require("react");
1668
1735
 
1669
- // ../../node_modules/.pnpm/react-image-crop@11.1.2_react@19.2.6/node_modules/react-image-crop/dist/index.js
1736
+ // ../../node_modules/.pnpm/react-image-crop@11.1.2_react@19.2.7/node_modules/react-image-crop/dist/index.js
1670
1737
  var import_react8 = __toESM(require("react"), 1);
1671
1738
  var r = {
1672
1739
  x: 0,
@@ -2095,7 +2162,7 @@ function styleInject(css, { insertAt } = {}) {
2095
2162
  }
2096
2163
  }
2097
2164
 
2098
- // ../../node_modules/.pnpm/react-image-crop@11.1.2_react@19.2.6/node_modules/react-image-crop/dist/ReactCrop.css
2165
+ // ../../node_modules/.pnpm/react-image-crop@11.1.2_react@19.2.7/node_modules/react-image-crop/dist/ReactCrop.css
2099
2166
  styleInject('@keyframes marching-ants {\n 0% {\n background-position:\n 0 0,\n 0 100%,\n 0 0,\n 100% 0;\n }\n to {\n background-position:\n 20px 0,\n -20px 100%,\n 0 -20px,\n 100% 20px;\n }\n}\n:root {\n --rc-drag-handle-size:12px;\n --rc-drag-handle-mobile-size:24px;\n --rc-drag-handle-bg-colour:#0003;\n --rc-drag-bar-size:6px;\n --rc-border-color:#ffffffb3;\n --rc-focus-color:#08f;\n}\n.ReactCrop {\n cursor: crosshair;\n max-width: 100%;\n display: inline-block;\n position: relative;\n}\n.ReactCrop *,\n.ReactCrop :before,\n.ReactCrop :after {\n box-sizing: border-box;\n}\n.ReactCrop--disabled,\n.ReactCrop--locked {\n cursor: inherit;\n}\n.ReactCrop__child-wrapper {\n max-height: inherit;\n overflow: hidden;\n}\n.ReactCrop__child-wrapper > img,\n.ReactCrop__child-wrapper > video {\n max-width: 100%;\n max-height: inherit;\n display: block;\n}\n.ReactCrop:not(.ReactCrop--disabled) .ReactCrop__child-wrapper > img,\n.ReactCrop:not(.ReactCrop--disabled) .ReactCrop__child-wrapper > video,\n.ReactCrop:not(.ReactCrop--disabled) .ReactCrop__crop-selection {\n touch-action: none;\n}\n.ReactCrop__crop-mask {\n pointer-events: none;\n width: calc(100% + .5px);\n height: calc(100% + .5px);\n position: absolute;\n inset: 0;\n}\n.ReactCrop__crop-selection {\n cursor: move;\n position: absolute;\n top: 0;\n left: 0;\n transform: translate(0, 0);\n}\n.ReactCrop--disabled .ReactCrop__crop-selection {\n cursor: inherit;\n}\n.ReactCrop--circular-crop .ReactCrop__crop-selection {\n border-radius: 50%;\n}\n.ReactCrop--circular-crop .ReactCrop__crop-selection:after {\n pointer-events: none;\n content: "";\n border: 1px solid var(--rc-border-color);\n opacity: .3;\n position: absolute;\n inset: -1px;\n}\n.ReactCrop--no-animate .ReactCrop__crop-selection {\n outline: 1px dashed #fff;\n}\n.ReactCrop__crop-selection:not(.ReactCrop--no-animate .ReactCrop__crop-selection) {\n color: #fff;\n background-image:\n linear-gradient(\n 90deg,\n #fff 50%,\n #444 50%),\n linear-gradient(\n 90deg,\n #fff 50%,\n #444 50%),\n linear-gradient(#fff 50%, #444 50%),\n linear-gradient(#fff 50%, #444 50%);\n background-position:\n 0 0,\n 0 100%,\n 0 0,\n 100% 0;\n background-repeat:\n repeat-x,\n repeat-x,\n repeat-y,\n repeat-y;\n background-size:\n 10px 1px,\n 10px 1px,\n 1px 10px,\n 1px 10px;\n animation: 1s linear infinite marching-ants;\n}\n.ReactCrop__crop-selection:focus {\n outline: 2px solid var(--rc-focus-color);\n outline-offset: -1px;\n}\n.ReactCrop--invisible-crop .ReactCrop__crop-mask,\n.ReactCrop--invisible-crop .ReactCrop__crop-selection {\n display: none;\n}\n.ReactCrop__rule-of-thirds-vt:before,\n.ReactCrop__rule-of-thirds-vt:after,\n.ReactCrop__rule-of-thirds-hz:before,\n.ReactCrop__rule-of-thirds-hz:after {\n content: "";\n background-color: #fff6;\n display: block;\n position: absolute;\n}\n.ReactCrop__rule-of-thirds-vt:before,\n.ReactCrop__rule-of-thirds-vt:after {\n width: 1px;\n height: 100%;\n}\n.ReactCrop__rule-of-thirds-vt:before {\n left: 33.3333%;\n}\n.ReactCrop__rule-of-thirds-vt:after {\n left: 66.6667%;\n}\n.ReactCrop__rule-of-thirds-hz:before,\n.ReactCrop__rule-of-thirds-hz:after {\n width: 100%;\n height: 1px;\n}\n.ReactCrop__rule-of-thirds-hz:before {\n top: 33.3333%;\n}\n.ReactCrop__rule-of-thirds-hz:after {\n top: 66.6667%;\n}\n.ReactCrop__drag-handle {\n width: var(--rc-drag-handle-size);\n height: var(--rc-drag-handle-size);\n background-color: var(--rc-drag-handle-bg-colour);\n border: 1px solid var(--rc-border-color);\n position: absolute;\n}\n.ReactCrop__drag-handle:focus {\n background: var(--rc-focus-color);\n}\n.ReactCrop .ord-nw {\n cursor: nw-resize;\n top: 0;\n left: 0;\n transform: translate(-50%, -50%);\n}\n.ReactCrop .ord-n {\n cursor: n-resize;\n top: 0;\n left: 50%;\n transform: translate(-50%, -50%);\n}\n.ReactCrop .ord-ne {\n cursor: ne-resize;\n top: 0;\n right: 0;\n transform: translate(50%, -50%);\n}\n.ReactCrop .ord-e {\n cursor: e-resize;\n top: 50%;\n right: 0;\n transform: translate(50%, -50%);\n}\n.ReactCrop .ord-se {\n cursor: se-resize;\n bottom: 0;\n right: 0;\n transform: translate(50%, 50%);\n}\n.ReactCrop .ord-s {\n cursor: s-resize;\n bottom: 0;\n left: 50%;\n transform: translate(-50%, 50%);\n}\n.ReactCrop .ord-sw {\n cursor: sw-resize;\n bottom: 0;\n left: 0;\n transform: translate(-50%, 50%);\n}\n.ReactCrop .ord-w {\n cursor: w-resize;\n top: 50%;\n left: 0;\n transform: translate(-50%, -50%);\n}\n.ReactCrop__disabled .ReactCrop__drag-handle {\n cursor: inherit;\n}\n.ReactCrop__drag-bar {\n position: absolute;\n}\n.ReactCrop__drag-bar.ord-n {\n width: 100%;\n height: var(--rc-drag-bar-size);\n top: 0;\n left: 0;\n transform: translateY(-50%);\n}\n.ReactCrop__drag-bar.ord-e {\n width: var(--rc-drag-bar-size);\n height: 100%;\n top: 0;\n right: 0;\n transform: translate(50%);\n}\n.ReactCrop__drag-bar.ord-s {\n width: 100%;\n height: var(--rc-drag-bar-size);\n bottom: 0;\n left: 0;\n transform: translateY(50%);\n}\n.ReactCrop__drag-bar.ord-w {\n width: var(--rc-drag-bar-size);\n height: 100%;\n top: 0;\n left: 0;\n transform: translate(-50%);\n}\n.ReactCrop--new-crop .ReactCrop__drag-bar,\n.ReactCrop--new-crop .ReactCrop__drag-handle,\n.ReactCrop--fixed-aspect .ReactCrop__drag-bar,\n.ReactCrop--fixed-aspect .ReactCrop__drag-handle.ord-n,\n.ReactCrop--fixed-aspect .ReactCrop__drag-handle.ord-e,\n.ReactCrop--fixed-aspect .ReactCrop__drag-handle.ord-s,\n.ReactCrop--fixed-aspect .ReactCrop__drag-handle.ord-w {\n display: none;\n}\n@media (pointer: coarse) {\n .ReactCrop .ord-n,\n .ReactCrop .ord-e,\n .ReactCrop .ord-s,\n .ReactCrop .ord-w {\n display: none;\n }\n .ReactCrop__drag-handle {\n width: var(--rc-drag-handle-mobile-size);\n height: var(--rc-drag-handle-mobile-size);\n }\n}\n');
2100
2167
 
2101
2168
  // src/components/crop-dialog.tsx
@@ -2342,69 +2409,6 @@ function CropDialog(props) {
2342
2409
  );
2343
2410
  }
2344
2411
 
2345
- // src/media-value.ts
2346
- var STRUCTURAL_MEDIA_KEYS = /* @__PURE__ */ new Set([
2347
- "assetId",
2348
- "versionId",
2349
- "url",
2350
- "metaSchemaVersion"
2351
- ]);
2352
- function isMediaValue(value) {
2353
- return typeof value === "object" && value !== null;
2354
- }
2355
- function makeMediaValue(input) {
2356
- if (!input.assetId || !input.versionId) {
2357
- return input.url;
2358
- }
2359
- const value = {
2360
- assetId: input.assetId,
2361
- versionId: input.versionId,
2362
- url: input.url
2363
- };
2364
- if (typeof input.metaSchemaVersion === "number") {
2365
- value.metaSchemaVersion = input.metaSchemaVersion;
2366
- }
2367
- if (input.metadata) {
2368
- for (const [k, v] of Object.entries(input.metadata)) {
2369
- if (v === void 0 || v === "") continue;
2370
- if (STRUCTURAL_MEDIA_KEYS.has(k)) continue;
2371
- value[k] = v;
2372
- }
2373
- }
2374
- return value;
2375
- }
2376
- function buildValueFromAsset(asset, crop) {
2377
- const metadata = { ...asset.metadata ?? {} };
2378
- if (typeof asset.width === "number") metadata.width = asset.width;
2379
- if (typeof asset.height === "number") metadata.height = asset.height;
2380
- return makeMediaValue({
2381
- assetId: asset.assetId,
2382
- versionId: asset.versionId,
2383
- url: buildValueWithCrop(getBaseUrl(asset.url), crop),
2384
- metaSchemaVersion: asset.metaSchemaVersion,
2385
- metadata
2386
- });
2387
- }
2388
- function applyCropToValue(value, crop) {
2389
- const currentUrl = isMediaValue(value) ? value.url : value;
2390
- const baseUrl = getBaseUrl(typeof currentUrl === "string" ? currentUrl : "");
2391
- if (!baseUrl) return value;
2392
- const url = buildValueWithCrop(baseUrl, crop);
2393
- return isMediaValue(value) ? { ...value, url } : url;
2394
- }
2395
- function applyTrimToValue(value, rect) {
2396
- const currentUrl = isMediaValue(value) ? value.url : value;
2397
- const baseUrl = getBaseUrl(typeof currentUrl === "string" ? currentUrl : "");
2398
- if (!baseUrl) return value;
2399
- const url = buildValueWithTrim(baseUrl, rect);
2400
- return isMediaValue(value) ? { ...value, url } : url;
2401
- }
2402
- function setMetaOnValue(value, fieldName, fieldValue) {
2403
- if (!isMediaValue(value)) return value;
2404
- if (STRUCTURAL_MEDIA_KEYS.has(fieldName)) return value;
2405
- return { ...value, [fieldName]: fieldValue };
2406
- }
2407
-
2408
2412
  // src/components/media-object-field.tsx
2409
2413
  var import_jsx_runtime10 = require("react/jsx-runtime");
2410
2414
  function MediaObjectFieldRender(props) {