@orion-studios/payload-studio 0.6.0-beta.22 → 0.6.0-beta.24

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.
@@ -4042,6 +4042,7 @@ function AdminStudioDashboard(rawProps) {
4042
4042
  // src/admin/components/studio/AdminStudioPagesListView.tsx
4043
4043
  var import_react19 = require("react");
4044
4044
  var import_link2 = __toESM(require("next/link"));
4045
+ var import_navigation3 = require("next/navigation");
4045
4046
  var import_ui7 = require("@payloadcms/ui");
4046
4047
 
4047
4048
  // src/admin/components/studio/AdminStudioNewPageView.tsx
@@ -4533,13 +4534,7 @@ var getPropString5 = (props, key, fallback) => {
4533
4534
  };
4534
4535
  function AdminStudioPagesListView(props) {
4535
4536
  const adminBasePath = useAdminBasePath();
4536
- const [pathname, setPathname] = (0, import_react19.useState)(null);
4537
- (0, import_react19.useEffect)(() => {
4538
- const updatePathname = () => setPathname(window.location.pathname);
4539
- updatePathname();
4540
- window.addEventListener("popstate", updatePathname);
4541
- return () => window.removeEventListener("popstate", updatePathname);
4542
- }, []);
4537
+ const pathname = (0, import_navigation3.usePathname)();
4543
4538
  const pagesPath = resolveAdminPath(adminBasePath, "/pages");
4544
4539
  const nestedPagePath = pathname && pathname.startsWith(`${pagesPath}/`) ? pathname.slice(`${pagesPath}/`.length).split("/")[0] : "";
4545
4540
  if (nestedPagePath === "new") {
@@ -6897,7 +6892,7 @@ function MediaListItem({
6897
6892
 
6898
6893
  // src/admin-app/components/MediaUploadForm.tsx
6899
6894
  var import_react27 = require("react");
6900
- var import_navigation3 = require("next/navigation");
6895
+ var import_navigation4 = require("next/navigation");
6901
6896
 
6902
6897
  // src/shared/clientImageUploadOptimization.ts
6903
6898
  var MAX_DIRECT_UPLOAD_BYTES = 4e6;
@@ -7032,7 +7027,7 @@ var parseUploadError = async (response) => {
7032
7027
  return fallback;
7033
7028
  };
7034
7029
  function MediaUploadForm() {
7035
- const router = (0, import_navigation3.useRouter)();
7030
+ const router = (0, import_navigation4.useRouter)();
7036
7031
  const fileInputRef = (0, import_react27.useRef)(null);
7037
7032
  const [alt, setAlt] = (0, import_react27.useState)("");
7038
7033
  const [file, setFile] = (0, import_react27.useState)(null);
@@ -2669,6 +2669,7 @@ function AdminStudioDashboard(rawProps) {
2669
2669
  // src/admin/components/studio/AdminStudioPagesListView.tsx
2670
2670
  import { useEffect as useEffect9, useMemo as useMemo5, useState as useState10 } from "react";
2671
2671
  import Link2 from "next/link";
2672
+ import { usePathname as usePathname3 } from "next/navigation";
2672
2673
  import { useAuth as useAuth5 } from "@payloadcms/ui";
2673
2674
 
2674
2675
  // src/admin/components/studio/AdminStudioNewPageView.tsx
@@ -3160,13 +3161,7 @@ var getPropString5 = (props, key, fallback) => {
3160
3161
  };
3161
3162
  function AdminStudioPagesListView(props) {
3162
3163
  const adminBasePath = useAdminBasePath();
3163
- const [pathname, setPathname] = useState10(null);
3164
- useEffect9(() => {
3165
- const updatePathname = () => setPathname(window.location.pathname);
3166
- updatePathname();
3167
- window.addEventListener("popstate", updatePathname);
3168
- return () => window.removeEventListener("popstate", updatePathname);
3169
- }, []);
3164
+ const pathname = usePathname3();
3170
3165
  const pagesPath = resolveAdminPath(adminBasePath, "/pages");
3171
3166
  const nestedPagePath = pathname && pathname.startsWith(`${pagesPath}/`) ? pathname.slice(`${pagesPath}/`.length).split("/")[0] : "";
3172
3167
  if (nestedPagePath === "new") {
@@ -2254,8 +2254,8 @@ function renderSimpleBlockPreview(args) {
2254
2254
  getImagePresentationStyle: getImagePresentationStyle2,
2255
2255
  index,
2256
2256
  isBlockUploadTarget,
2257
- normalizeImageCornerStyle: normalizeImageCornerStyle2,
2258
- normalizeImageFit: normalizeImageFit2,
2257
+ normalizeImageCornerStyle: normalizeImageCornerStyle3,
2258
+ normalizeImageFit: normalizeImageFit3,
2259
2259
  normalizeText: normalizeText3,
2260
2260
  onDropAt,
2261
2261
  renderWithSectionShell,
@@ -2439,8 +2439,8 @@ function renderSimpleBlockPreview(args) {
2439
2439
  const imagePositionX = parseOptionalPercentNumber(mediaSettings.positionX ?? block?.imagePositionX);
2440
2440
  const imagePositionY = parseOptionalPercentNumber(mediaSettings.positionY ?? block?.imagePositionY);
2441
2441
  const imageStyle = getImagePresentationStyle2({
2442
- cornerStyle: normalizeImageCornerStyle2(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2443
- fit: normalizeImageFit2(mediaSettings.fit ?? block?.imageFit),
2442
+ cornerStyle: normalizeImageCornerStyle3(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2443
+ fit: normalizeImageFit3(mediaSettings.fit ?? block?.imageFit),
2444
2444
  positionX: imagePositionX,
2445
2445
  positionY: imagePositionY
2446
2446
  });
@@ -2970,6 +2970,57 @@ var normalizeNumber2 = (value, fallback) => {
2970
2970
  return fallback;
2971
2971
  };
2972
2972
  var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2973
+ var getRelationID2 = (value) => {
2974
+ if (typeof value === "number" || typeof value === "string") {
2975
+ return value;
2976
+ }
2977
+ if (!isRecord5(value)) {
2978
+ return null;
2979
+ }
2980
+ const id = value.id;
2981
+ return typeof id === "number" || typeof id === "string" ? id : null;
2982
+ };
2983
+ var toMediaLibraryItem2 = (value) => {
2984
+ if (!isRecord5(value)) {
2985
+ return null;
2986
+ }
2987
+ const id = getRelationID2(value);
2988
+ if (id === null) {
2989
+ return null;
2990
+ }
2991
+ const filename = typeof value.filename === "string" ? value.filename : "";
2992
+ const url = typeof value.url === "string" && value.url.length > 0 ? value.url : filename ? `/api/media/file/${encodeURIComponent(filename)}` : "";
2993
+ return {
2994
+ alt: typeof value.alt === "string" ? value.alt : "",
2995
+ filename,
2996
+ id,
2997
+ url
2998
+ };
2999
+ };
3000
+ var mediaLabel2 = (item) => item.filename || item.alt || `Media #${item.id}`;
3001
+ var normalizeImageFit = (value) => value === "contain" ? "contain" : "cover";
3002
+ var normalizeImageCornerStyle = (value) => value === "square" ? "square" : "rounded";
3003
+ var getItemMediaSettings = (item) => {
3004
+ const settings = isRecord5(item.settings) ? item.settings : {};
3005
+ const media = isRecord5(settings.media) ? settings.media : {};
3006
+ return {
3007
+ cornerStyle: normalizeImageCornerStyle(media.cornerStyle ?? item.imageCornerStyle),
3008
+ fit: normalizeImageFit(media.fit ?? item.imageFit),
3009
+ height: (() => {
3010
+ const value = media.height ?? item.imageHeight;
3011
+ if (typeof value === "number" && Number.isFinite(value)) {
3012
+ return value;
3013
+ }
3014
+ if (typeof value === "string" && value.trim().length > 0) {
3015
+ const parsed = Number(value);
3016
+ return Number.isFinite(parsed) ? parsed : null;
3017
+ }
3018
+ return null;
3019
+ })(),
3020
+ positionX: clamp(normalizeNumber2(media.positionX ?? item.imagePositionX, 50), 0, 100),
3021
+ positionY: clamp(normalizeNumber2(media.positionY ?? item.imagePositionY, 50), 0, 100)
3022
+ };
3023
+ };
2973
3024
  var hasQueryMatch = (query, ...values) => {
2974
3025
  const normalized = query.trim().toLowerCase();
2975
3026
  if (!normalized) {
@@ -3016,18 +3067,152 @@ var bulletsToTextareaValue = (value) => {
3016
3067
  return value.map((item) => isRecord5(item) && typeof item.label === "string" ? item.label.trim() : "").filter(Boolean).join("\n");
3017
3068
  };
3018
3069
  var textareaValueToBullets = (value) => value.split("\n").map((line) => line.trim()).filter(Boolean).map((label) => ({ label }));
3070
+ function ItemMediaControl({
3071
+ fieldName,
3072
+ imageURLFieldName,
3073
+ item,
3074
+ itemIndex,
3075
+ label,
3076
+ mediaLibrary,
3077
+ mediaLibraryError,
3078
+ mediaLibraryLoading,
3079
+ maxHeight,
3080
+ minHeight,
3081
+ onRemoveItemMedia,
3082
+ onSelectItemMedia,
3083
+ onUpdateItemField,
3084
+ onUpdateItemMediaPresentation,
3085
+ onUploadItemMedia,
3086
+ searchQuery,
3087
+ uploadDisabled,
3088
+ uploadLabel,
3089
+ uploading
3090
+ }) {
3091
+ const selectedMedia = toMediaLibraryItem2(item[fieldName]);
3092
+ const selectedMediaID = getRelationID2(item[fieldName]);
3093
+ const directImageURL = normalizeText(item[imageURLFieldName]);
3094
+ const previewURL = selectedMedia?.url || directImageURL;
3095
+ const mediaSettings = getItemMediaSettings(item);
3096
+ const sourceOptions = selectedMedia && !mediaLibrary.some((libraryItem) => String(libraryItem.id) === String(selectedMedia.id)) ? [selectedMedia, ...mediaLibrary] : mediaLibrary;
3097
+ if (!hasQueryMatch(searchQuery, label, "image", "media", "photo", "picture", "url", "fit", "crop", "height")) {
3098
+ return null;
3099
+ }
3100
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-item-card", style: { padding: "0.56rem" }, children: [
3101
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-label", children: label }),
3102
+ previewURL ? (
3103
+ // eslint-disable-next-line @next/next/no-img-element
3104
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3105
+ "img",
3106
+ {
3107
+ alt: selectedMedia?.alt || label,
3108
+ src: previewURL,
3109
+ style: {
3110
+ aspectRatio: "16 / 9",
3111
+ border: "1px solid rgba(35, 51, 82, 0.14)",
3112
+ borderRadius: mediaSettings.cornerStyle === "square" ? 4 : 10,
3113
+ display: "block",
3114
+ objectFit: mediaSettings.fit,
3115
+ objectPosition: `${mediaSettings.positionX}% ${mediaSettings.positionY}%`,
3116
+ width: "100%"
3117
+ }
3118
+ }
3119
+ )
3120
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-empty", children: "No image selected." }),
3121
+ mediaLibraryLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-note", children: "Loading media library..." }) : null,
3122
+ mediaLibraryError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-error", children: mediaLibraryError }) : null,
3123
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3124
+ "Media Library Image",
3125
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3126
+ "select",
3127
+ {
3128
+ className: "orion-builder-settings-input",
3129
+ onChange: (event) => onSelectItemMedia?.(itemIndex, fieldName, event.target.value),
3130
+ value: selectedMediaID !== null ? String(selectedMediaID) : "",
3131
+ children: [
3132
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: "", children: "No library image" }),
3133
+ sourceOptions.map((libraryItem) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: String(libraryItem.id), children: mediaLabel2(libraryItem) }, String(libraryItem.id)))
3134
+ ]
3135
+ }
3136
+ )
3137
+ ] }),
3138
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3139
+ "button",
3140
+ {
3141
+ className: "orion-builder-settings-inline-btn",
3142
+ disabled: selectedMediaID === null,
3143
+ onClick: () => onRemoveItemMedia?.(itemIndex, fieldName),
3144
+ type: "button",
3145
+ children: "Remove Library Image"
3146
+ }
3147
+ ),
3148
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3149
+ "Direct Image URL",
3150
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3151
+ "input",
3152
+ {
3153
+ className: "orion-builder-settings-input",
3154
+ onChange: (event) => onUpdateItemField(itemIndex, imageURLFieldName, event.target.value),
3155
+ type: "text",
3156
+ value: directImageURL
3157
+ }
3158
+ )
3159
+ ] }),
3160
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3161
+ uploadLabel,
3162
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3163
+ "input",
3164
+ {
3165
+ accept: "image/*",
3166
+ className: "orion-builder-settings-input",
3167
+ disabled: uploadDisabled,
3168
+ onChange: (event) => {
3169
+ const file = event.currentTarget.files?.[0];
3170
+ if (file) {
3171
+ onUploadItemMedia?.(itemIndex, fieldName, file);
3172
+ }
3173
+ event.currentTarget.value = "";
3174
+ },
3175
+ type: "file"
3176
+ }
3177
+ )
3178
+ ] }),
3179
+ uploading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-note", children: "Uploading image..." }) : null,
3180
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3181
+ ImageControls,
3182
+ {
3183
+ cornerStyle: mediaSettings.cornerStyle,
3184
+ fit: mediaSettings.fit,
3185
+ height: mediaSettings.height,
3186
+ maxHeight,
3187
+ minHeight,
3188
+ onChange: (field, value) => onUpdateItemMediaPresentation?.(itemIndex, field, value),
3189
+ positionX: mediaSettings.positionX,
3190
+ positionY: mediaSettings.positionY
3191
+ }
3192
+ )
3193
+ ] });
3194
+ }
3019
3195
  function ArrayItemsEditor({
3020
3196
  blockType,
3021
3197
  expandedItemIndex,
3022
3198
  items,
3199
+ mediaLibrary = [],
3200
+ mediaLibraryError = "",
3201
+ mediaLibraryLoading = false,
3023
3202
  mode,
3024
3203
  onAddItem,
3204
+ onRemoveItemMedia,
3025
3205
  onRemoveItem,
3206
+ onSelectItemMedia,
3026
3207
  onToggleItem,
3027
3208
  onUpdateItemField,
3209
+ onUpdateItemMediaPresentation,
3028
3210
  onUpdateItemSetting,
3211
+ onUploadItemMedia,
3029
3212
  searchQuery,
3030
- showInlineCopyFields
3213
+ showInlineCopyFields,
3214
+ uploadDisabled = false,
3215
+ isItemMediaUploading
3031
3216
  }) {
3032
3217
  const config = blockConfig[blockType];
3033
3218
  const normalizedQuery = searchQuery.trim().toLowerCase();
@@ -3177,11 +3362,30 @@ function ArrayItemsEditor({
3177
3362
  }
3178
3363
  )
3179
3364
  ] }) : null,
3180
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-note", children: [
3181
- "Select this item and use the ",
3182
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Media" }),
3183
- " group above for image source and presentation."
3184
- ] })
3365
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3366
+ ItemMediaControl,
3367
+ {
3368
+ fieldName: "media",
3369
+ imageURLFieldName: "imageURL",
3370
+ item,
3371
+ itemIndex,
3372
+ label: "Feature Image",
3373
+ maxHeight: 600,
3374
+ mediaLibrary,
3375
+ mediaLibraryError,
3376
+ mediaLibraryLoading,
3377
+ minHeight: 40,
3378
+ onRemoveItemMedia,
3379
+ onSelectItemMedia,
3380
+ onUpdateItemField,
3381
+ onUpdateItemMediaPresentation,
3382
+ onUploadItemMedia,
3383
+ searchQuery: normalizedQuery,
3384
+ uploadDisabled,
3385
+ uploadLabel: "Upload Feature Image",
3386
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3387
+ }
3388
+ )
3185
3389
  ] }) : null,
3186
3390
  blockType === "logoWall" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3187
3391
  showInlineCopyFields ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
@@ -3208,11 +3412,30 @@ function ArrayItemsEditor({
3208
3412
  }
3209
3413
  )
3210
3414
  ] }) : null,
3211
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-note", children: [
3212
- "Select this item and use the ",
3213
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Media" }),
3214
- " group above for image source and presentation."
3215
- ] })
3415
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3416
+ ItemMediaControl,
3417
+ {
3418
+ fieldName: "media",
3419
+ imageURLFieldName: "imageURL",
3420
+ item,
3421
+ itemIndex,
3422
+ label: "Logo Image",
3423
+ maxHeight: 200,
3424
+ mediaLibrary,
3425
+ mediaLibraryError,
3426
+ mediaLibraryLoading,
3427
+ minHeight: 24,
3428
+ onRemoveItemMedia,
3429
+ onSelectItemMedia,
3430
+ onUpdateItemField,
3431
+ onUpdateItemMediaPresentation,
3432
+ onUploadItemMedia,
3433
+ searchQuery: normalizedQuery,
3434
+ uploadDisabled,
3435
+ uploadLabel: "Upload Logo Image",
3436
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3437
+ }
3438
+ )
3216
3439
  ] }) : null,
3217
3440
  blockType === "beforeAfter" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3218
3441
  showInlineCopyFields ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
@@ -3240,11 +3463,54 @@ function ArrayItemsEditor({
3240
3463
  )
3241
3464
  ] })
3242
3465
  ] }) : null,
3243
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-note", children: [
3244
- "Select this item and use the ",
3245
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Media" }),
3246
- " group above for before/after image source and presentation."
3247
- ] })
3466
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3467
+ ItemMediaControl,
3468
+ {
3469
+ fieldName: "beforeMedia",
3470
+ imageURLFieldName: "beforeImageURL",
3471
+ item,
3472
+ itemIndex,
3473
+ label: "Before Image",
3474
+ maxHeight: 600,
3475
+ mediaLibrary,
3476
+ mediaLibraryError,
3477
+ mediaLibraryLoading,
3478
+ minHeight: 60,
3479
+ onRemoveItemMedia,
3480
+ onSelectItemMedia,
3481
+ onUpdateItemField,
3482
+ onUpdateItemMediaPresentation,
3483
+ onUploadItemMedia,
3484
+ searchQuery: normalizedQuery,
3485
+ uploadDisabled,
3486
+ uploadLabel: "Upload Before Image",
3487
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "beforeMedia"))
3488
+ }
3489
+ ),
3490
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3491
+ ItemMediaControl,
3492
+ {
3493
+ fieldName: "afterMedia",
3494
+ imageURLFieldName: "afterImageURL",
3495
+ item,
3496
+ itemIndex,
3497
+ label: "After Image",
3498
+ maxHeight: 600,
3499
+ mediaLibrary,
3500
+ mediaLibraryError,
3501
+ mediaLibraryLoading,
3502
+ minHeight: 60,
3503
+ onRemoveItemMedia,
3504
+ onSelectItemMedia,
3505
+ onUpdateItemField,
3506
+ onUpdateItemMediaPresentation,
3507
+ onUploadItemMedia,
3508
+ searchQuery: normalizedQuery,
3509
+ uploadDisabled,
3510
+ uploadLabel: "Upload After Image",
3511
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "afterMedia"))
3512
+ }
3513
+ )
3248
3514
  ] }) : null,
3249
3515
  blockType === "stats" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_jsx_runtime12.Fragment, { children: showInlineCopyFields ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3250
3516
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
@@ -3450,8 +3716,8 @@ var resolveBuilderMediumHeroHeight = (topViewportHeight) => {
3450
3716
  }
3451
3717
  return "50svh";
3452
3718
  };
3453
- var normalizeImageFit = (value) => normalizeHeroImageFit(value);
3454
- var normalizeImageCornerStyle = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3719
+ var normalizeImageFit2 = (value) => normalizeHeroImageFit(value);
3720
+ var normalizeImageCornerStyle2 = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3455
3721
  var positionPercent = (value, fit) => {
3456
3722
  const resolved = fit === "cover" && (value === "left" || value === "right") ? "center" : value;
3457
3723
  switch (resolved) {
@@ -3982,7 +4248,7 @@ var sectionStyleFromBlock = (block, pageDefaults) => {
3982
4248
  sectionStyle: sectionMode === "color" ? { background: sectionColor } : sectionMode === "gradient" ? { background: sectionGradient } : block.blockType === "hero" ? { background: "transparent" } : {}
3983
4249
  };
3984
4250
  };
3985
- function getRelationID2(value) {
4251
+ function getRelationID3(value) {
3986
4252
  if (typeof value === "number" || typeof value === "string") {
3987
4253
  return value;
3988
4254
  }
@@ -4015,7 +4281,7 @@ function extractUploadedMedia(value) {
4015
4281
  if (!candidate || typeof candidate !== "object") {
4016
4282
  return null;
4017
4283
  }
4018
- const id = getRelationID2(candidate);
4284
+ const id = getRelationID3(candidate);
4019
4285
  if (id === null) {
4020
4286
  return null;
4021
4287
  }
@@ -4026,11 +4292,11 @@ function extractUploadedMedia(value) {
4026
4292
  url: typeof candidate.url === "string" ? candidate.url : ""
4027
4293
  };
4028
4294
  }
4029
- function toMediaLibraryItem2(value) {
4295
+ function toMediaLibraryItem3(value) {
4030
4296
  if (!value || typeof value !== "object") {
4031
4297
  return null;
4032
4298
  }
4033
- const id = getRelationID2(value);
4299
+ const id = getRelationID3(value);
4034
4300
  if (id === null) {
4035
4301
  return null;
4036
4302
  }
@@ -4208,7 +4474,7 @@ function BuilderPageEditor({
4208
4474
  }
4209
4475
  const json = await response.json();
4210
4476
  const docs = Array.isArray(json.docs) ? json.docs : [];
4211
- const items = docs.map((doc2) => toMediaLibraryItem2(doc2)).filter((item) => item !== null);
4477
+ const items = docs.map((doc2) => toMediaLibraryItem3(doc2)).filter((item) => item !== null);
4212
4478
  setMediaLibrary(items);
4213
4479
  } catch (error) {
4214
4480
  setMediaLibraryError(error instanceof Error ? error.message : "Could not load media library.");
@@ -4562,7 +4828,7 @@ function BuilderPageEditor({
4562
4828
  const nextLayout = cloneBlockLayout(layout);
4563
4829
  const block = nextLayout[selectedIndex];
4564
4830
  if (target.kind === "hero") {
4565
- const uploadedItem = toMediaLibraryItem2(uploaded);
4831
+ const uploadedItem = toMediaLibraryItem3(uploaded);
4566
4832
  nextLayout[selectedIndex] = {
4567
4833
  ...block,
4568
4834
  backgroundImageURL: uploadedItem?.url || normalizeText2(uploaded.url),
@@ -4626,13 +4892,13 @@ function BuilderPageEditor({
4626
4892
  const nextBlock = { ...migrateBlockToSettingsV2(block) };
4627
4893
  const blockType = normalizeText2(nextBlock.blockType);
4628
4894
  if (blockType === "hero") {
4629
- const mediaID = getRelationID2(nextBlock.media);
4895
+ const mediaID = getRelationID3(nextBlock.media);
4630
4896
  if (mediaID !== null) {
4631
4897
  nextBlock.media = mediaID;
4632
4898
  }
4633
4899
  }
4634
4900
  if (blockType === "media") {
4635
- const imageID = getRelationID2(nextBlock.image);
4901
+ const imageID = getRelationID3(nextBlock.image);
4636
4902
  if (imageID !== null) {
4637
4903
  nextBlock.image = imageID;
4638
4904
  }
@@ -4644,15 +4910,15 @@ function BuilderPageEditor({
4644
4910
  return rawItem;
4645
4911
  }
4646
4912
  const nextItem = { ...rawItem };
4647
- const mediaID = getRelationID2(nextItem.media);
4913
+ const mediaID = getRelationID3(nextItem.media);
4648
4914
  if (mediaID !== null) {
4649
4915
  nextItem.media = mediaID;
4650
4916
  }
4651
- const beforeMediaID = getRelationID2(nextItem.beforeMedia);
4917
+ const beforeMediaID = getRelationID3(nextItem.beforeMedia);
4652
4918
  if (beforeMediaID !== null) {
4653
4919
  nextItem.beforeMedia = beforeMediaID;
4654
4920
  }
4655
- const afterMediaID = getRelationID2(nextItem.afterMedia);
4921
+ const afterMediaID = getRelationID3(nextItem.afterMedia);
4656
4922
  if (afterMediaID !== null) {
4657
4923
  nextItem.afterMedia = afterMediaID;
4658
4924
  }
@@ -5230,8 +5496,8 @@ function BuilderPageEditor({
5230
5496
  }
5231
5497
  ] : [];
5232
5498
  const selectedMediaImageControls = selectedItemRecord && typeof selectedItemIndex === "number" && (selectedType === "featureGrid" || selectedType === "logoWall" || selectedType === "beforeAfter") ? {
5233
- cornerStyle: normalizeImageCornerStyle(selectedItemRecord.imageCornerStyle),
5234
- fit: normalizeImageFit(selectedItemRecord.imageFit),
5499
+ cornerStyle: normalizeImageCornerStyle2(selectedItemRecord.imageCornerStyle),
5500
+ fit: normalizeImageFit2(selectedItemRecord.imageFit),
5235
5501
  height: (() => {
5236
5502
  if (typeof selectedItemRecord.imageHeight === "number" && Number.isFinite(selectedItemRecord.imageHeight)) {
5237
5503
  return selectedItemRecord.imageHeight;
@@ -5706,8 +5972,8 @@ function BuilderPageEditor({
5706
5972
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5707
5973
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5708
5974
  const itemImageStyle = getImagePresentationStyle({
5709
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5710
- fit: normalizeImageFit(itemRecord?.imageFit),
5975
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5976
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5711
5977
  positionX: itemPositionX,
5712
5978
  positionY: itemPositionY
5713
5979
  });
@@ -5824,8 +6090,8 @@ function BuilderPageEditor({
5824
6090
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5825
6091
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5826
6092
  const itemImageStyle = getImagePresentationStyle({
5827
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5828
- fit: normalizeImageFit(itemRecord?.imageFit),
6093
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6094
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5829
6095
  positionX: itemPositionX,
5830
6096
  positionY: itemPositionY
5831
6097
  });
@@ -6031,8 +6297,8 @@ function BuilderPageEditor({
6031
6297
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
6032
6298
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
6033
6299
  const imageStyle = getImagePresentationStyle({
6034
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
6035
- fit: normalizeImageFit(itemRecord?.imageFit),
6300
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6301
+ fit: normalizeImageFit2(itemRecord?.imageFit),
6036
6302
  positionX: itemPositionX,
6037
6303
  positionY: itemPositionY,
6038
6304
  roundedRadius: 10
@@ -6145,8 +6411,8 @@ function BuilderPageEditor({
6145
6411
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
6146
6412
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
6147
6413
  const imageStyle = getImagePresentationStyle({
6148
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
6149
- fit: normalizeImageFit(itemRecord?.imageFit),
6414
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6415
+ fit: normalizeImageFit2(itemRecord?.imageFit),
6150
6416
  positionX: itemPositionX,
6151
6417
  positionY: itemPositionY
6152
6418
  });
@@ -6452,8 +6718,8 @@ function BuilderPageEditor({
6452
6718
  getImagePresentationStyle,
6453
6719
  index,
6454
6720
  isBlockUploadTarget,
6455
- normalizeImageCornerStyle,
6456
- normalizeImageFit,
6721
+ normalizeImageCornerStyle: normalizeImageCornerStyle2,
6722
+ normalizeImageFit: normalizeImageFit2,
6457
6723
  normalizeText: normalizeText2,
6458
6724
  onDropAt,
6459
6725
  renderWithSectionShell,
@@ -6682,14 +6948,23 @@ function BuilderPageEditor({
6682
6948
  blockType: selectedType,
6683
6949
  expandedItemIndex,
6684
6950
  items: selectedItems,
6951
+ isItemMediaUploading: isSelectedItemMediaUploading,
6952
+ mediaLibrary,
6953
+ mediaLibraryError,
6954
+ mediaLibraryLoading,
6685
6955
  mode: settingsPanelMode,
6686
6956
  onAddItem: appendDefaultItemToSelected,
6687
6957
  onRemoveItem: (itemIndex) => removeItemFromSelected("items", itemIndex),
6958
+ onRemoveItemMedia: (itemIndex, fieldName) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, ""),
6959
+ onSelectItemMedia: (itemIndex, fieldName, mediaID) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, mediaID),
6688
6960
  onToggleItem: toggleSelectedItem,
6689
6961
  onUpdateItemField: (itemIndex, fieldName, value) => updateArrayItemField(selectedIndex ?? 0, "items", itemIndex, fieldName, value),
6962
+ onUpdateItemMediaPresentation: (itemIndex, fieldName, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, `media.${fieldName}`, value),
6690
6963
  onUpdateItemSetting: (itemIndex, path, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, path, value),
6964
+ onUploadItemMedia: uploadItemMediaFromV2,
6691
6965
  searchQuery: settingsSearchQuery,
6692
- showInlineCopyFields: editCopyInPanelEnabled
6966
+ showInlineCopyFields: editCopyInPanelEnabled,
6967
+ uploadDisabled: uploadingTarget !== null
6693
6968
  }
6694
6969
  ) : null,
6695
6970
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { style: sidebarLabelStyle, children: [
@@ -2134,8 +2134,8 @@ function renderSimpleBlockPreview(args) {
2134
2134
  getImagePresentationStyle: getImagePresentationStyle2,
2135
2135
  index,
2136
2136
  isBlockUploadTarget,
2137
- normalizeImageCornerStyle: normalizeImageCornerStyle2,
2138
- normalizeImageFit: normalizeImageFit2,
2137
+ normalizeImageCornerStyle: normalizeImageCornerStyle3,
2138
+ normalizeImageFit: normalizeImageFit3,
2139
2139
  normalizeText: normalizeText3,
2140
2140
  onDropAt,
2141
2141
  renderWithSectionShell,
@@ -2319,8 +2319,8 @@ function renderSimpleBlockPreview(args) {
2319
2319
  const imagePositionX = parseOptionalPercentNumber(mediaSettings.positionX ?? block?.imagePositionX);
2320
2320
  const imagePositionY = parseOptionalPercentNumber(mediaSettings.positionY ?? block?.imagePositionY);
2321
2321
  const imageStyle = getImagePresentationStyle2({
2322
- cornerStyle: normalizeImageCornerStyle2(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2323
- fit: normalizeImageFit2(mediaSettings.fit ?? block?.imageFit),
2322
+ cornerStyle: normalizeImageCornerStyle3(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2323
+ fit: normalizeImageFit3(mediaSettings.fit ?? block?.imageFit),
2324
2324
  positionX: imagePositionX,
2325
2325
  positionY: imagePositionY
2326
2326
  });
@@ -2850,6 +2850,57 @@ var normalizeNumber2 = (value, fallback) => {
2850
2850
  return fallback;
2851
2851
  };
2852
2852
  var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2853
+ var getRelationID2 = (value) => {
2854
+ if (typeof value === "number" || typeof value === "string") {
2855
+ return value;
2856
+ }
2857
+ if (!isRecord5(value)) {
2858
+ return null;
2859
+ }
2860
+ const id = value.id;
2861
+ return typeof id === "number" || typeof id === "string" ? id : null;
2862
+ };
2863
+ var toMediaLibraryItem2 = (value) => {
2864
+ if (!isRecord5(value)) {
2865
+ return null;
2866
+ }
2867
+ const id = getRelationID2(value);
2868
+ if (id === null) {
2869
+ return null;
2870
+ }
2871
+ const filename = typeof value.filename === "string" ? value.filename : "";
2872
+ const url = typeof value.url === "string" && value.url.length > 0 ? value.url : filename ? `/api/media/file/${encodeURIComponent(filename)}` : "";
2873
+ return {
2874
+ alt: typeof value.alt === "string" ? value.alt : "",
2875
+ filename,
2876
+ id,
2877
+ url
2878
+ };
2879
+ };
2880
+ var mediaLabel2 = (item) => item.filename || item.alt || `Media #${item.id}`;
2881
+ var normalizeImageFit = (value) => value === "contain" ? "contain" : "cover";
2882
+ var normalizeImageCornerStyle = (value) => value === "square" ? "square" : "rounded";
2883
+ var getItemMediaSettings = (item) => {
2884
+ const settings = isRecord5(item.settings) ? item.settings : {};
2885
+ const media = isRecord5(settings.media) ? settings.media : {};
2886
+ return {
2887
+ cornerStyle: normalizeImageCornerStyle(media.cornerStyle ?? item.imageCornerStyle),
2888
+ fit: normalizeImageFit(media.fit ?? item.imageFit),
2889
+ height: (() => {
2890
+ const value = media.height ?? item.imageHeight;
2891
+ if (typeof value === "number" && Number.isFinite(value)) {
2892
+ return value;
2893
+ }
2894
+ if (typeof value === "string" && value.trim().length > 0) {
2895
+ const parsed = Number(value);
2896
+ return Number.isFinite(parsed) ? parsed : null;
2897
+ }
2898
+ return null;
2899
+ })(),
2900
+ positionX: clamp(normalizeNumber2(media.positionX ?? item.imagePositionX, 50), 0, 100),
2901
+ positionY: clamp(normalizeNumber2(media.positionY ?? item.imagePositionY, 50), 0, 100)
2902
+ };
2903
+ };
2853
2904
  var hasQueryMatch = (query, ...values) => {
2854
2905
  const normalized = query.trim().toLowerCase();
2855
2906
  if (!normalized) {
@@ -2896,18 +2947,152 @@ var bulletsToTextareaValue = (value) => {
2896
2947
  return value.map((item) => isRecord5(item) && typeof item.label === "string" ? item.label.trim() : "").filter(Boolean).join("\n");
2897
2948
  };
2898
2949
  var textareaValueToBullets = (value) => value.split("\n").map((line) => line.trim()).filter(Boolean).map((label) => ({ label }));
2950
+ function ItemMediaControl({
2951
+ fieldName,
2952
+ imageURLFieldName,
2953
+ item,
2954
+ itemIndex,
2955
+ label,
2956
+ mediaLibrary,
2957
+ mediaLibraryError,
2958
+ mediaLibraryLoading,
2959
+ maxHeight,
2960
+ minHeight,
2961
+ onRemoveItemMedia,
2962
+ onSelectItemMedia,
2963
+ onUpdateItemField,
2964
+ onUpdateItemMediaPresentation,
2965
+ onUploadItemMedia,
2966
+ searchQuery,
2967
+ uploadDisabled,
2968
+ uploadLabel,
2969
+ uploading
2970
+ }) {
2971
+ const selectedMedia = toMediaLibraryItem2(item[fieldName]);
2972
+ const selectedMediaID = getRelationID2(item[fieldName]);
2973
+ const directImageURL = normalizeText(item[imageURLFieldName]);
2974
+ const previewURL = selectedMedia?.url || directImageURL;
2975
+ const mediaSettings = getItemMediaSettings(item);
2976
+ const sourceOptions = selectedMedia && !mediaLibrary.some((libraryItem) => String(libraryItem.id) === String(selectedMedia.id)) ? [selectedMedia, ...mediaLibrary] : mediaLibrary;
2977
+ if (!hasQueryMatch(searchQuery, label, "image", "media", "photo", "picture", "url", "fit", "crop", "height")) {
2978
+ return null;
2979
+ }
2980
+ return /* @__PURE__ */ jsxs11("div", { className: "orion-builder-settings-item-card", style: { padding: "0.56rem" }, children: [
2981
+ /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-label", children: label }),
2982
+ previewURL ? (
2983
+ // eslint-disable-next-line @next/next/no-img-element
2984
+ /* @__PURE__ */ jsx12(
2985
+ "img",
2986
+ {
2987
+ alt: selectedMedia?.alt || label,
2988
+ src: previewURL,
2989
+ style: {
2990
+ aspectRatio: "16 / 9",
2991
+ border: "1px solid rgba(35, 51, 82, 0.14)",
2992
+ borderRadius: mediaSettings.cornerStyle === "square" ? 4 : 10,
2993
+ display: "block",
2994
+ objectFit: mediaSettings.fit,
2995
+ objectPosition: `${mediaSettings.positionX}% ${mediaSettings.positionY}%`,
2996
+ width: "100%"
2997
+ }
2998
+ }
2999
+ )
3000
+ ) : /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-empty", children: "No image selected." }),
3001
+ mediaLibraryLoading ? /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-note", children: "Loading media library..." }) : null,
3002
+ mediaLibraryError ? /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-error", children: mediaLibraryError }) : null,
3003
+ /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
3004
+ "Media Library Image",
3005
+ /* @__PURE__ */ jsxs11(
3006
+ "select",
3007
+ {
3008
+ className: "orion-builder-settings-input",
3009
+ onChange: (event) => onSelectItemMedia?.(itemIndex, fieldName, event.target.value),
3010
+ value: selectedMediaID !== null ? String(selectedMediaID) : "",
3011
+ children: [
3012
+ /* @__PURE__ */ jsx12("option", { value: "", children: "No library image" }),
3013
+ sourceOptions.map((libraryItem) => /* @__PURE__ */ jsx12("option", { value: String(libraryItem.id), children: mediaLabel2(libraryItem) }, String(libraryItem.id)))
3014
+ ]
3015
+ }
3016
+ )
3017
+ ] }),
3018
+ /* @__PURE__ */ jsx12(
3019
+ "button",
3020
+ {
3021
+ className: "orion-builder-settings-inline-btn",
3022
+ disabled: selectedMediaID === null,
3023
+ onClick: () => onRemoveItemMedia?.(itemIndex, fieldName),
3024
+ type: "button",
3025
+ children: "Remove Library Image"
3026
+ }
3027
+ ),
3028
+ /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
3029
+ "Direct Image URL",
3030
+ /* @__PURE__ */ jsx12(
3031
+ "input",
3032
+ {
3033
+ className: "orion-builder-settings-input",
3034
+ onChange: (event) => onUpdateItemField(itemIndex, imageURLFieldName, event.target.value),
3035
+ type: "text",
3036
+ value: directImageURL
3037
+ }
3038
+ )
3039
+ ] }),
3040
+ /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
3041
+ uploadLabel,
3042
+ /* @__PURE__ */ jsx12(
3043
+ "input",
3044
+ {
3045
+ accept: "image/*",
3046
+ className: "orion-builder-settings-input",
3047
+ disabled: uploadDisabled,
3048
+ onChange: (event) => {
3049
+ const file = event.currentTarget.files?.[0];
3050
+ if (file) {
3051
+ onUploadItemMedia?.(itemIndex, fieldName, file);
3052
+ }
3053
+ event.currentTarget.value = "";
3054
+ },
3055
+ type: "file"
3056
+ }
3057
+ )
3058
+ ] }),
3059
+ uploading ? /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-note", children: "Uploading image..." }) : null,
3060
+ /* @__PURE__ */ jsx12(
3061
+ ImageControls,
3062
+ {
3063
+ cornerStyle: mediaSettings.cornerStyle,
3064
+ fit: mediaSettings.fit,
3065
+ height: mediaSettings.height,
3066
+ maxHeight,
3067
+ minHeight,
3068
+ onChange: (field, value) => onUpdateItemMediaPresentation?.(itemIndex, field, value),
3069
+ positionX: mediaSettings.positionX,
3070
+ positionY: mediaSettings.positionY
3071
+ }
3072
+ )
3073
+ ] });
3074
+ }
2899
3075
  function ArrayItemsEditor({
2900
3076
  blockType,
2901
3077
  expandedItemIndex,
2902
3078
  items,
3079
+ mediaLibrary = [],
3080
+ mediaLibraryError = "",
3081
+ mediaLibraryLoading = false,
2903
3082
  mode,
2904
3083
  onAddItem,
3084
+ onRemoveItemMedia,
2905
3085
  onRemoveItem,
3086
+ onSelectItemMedia,
2906
3087
  onToggleItem,
2907
3088
  onUpdateItemField,
3089
+ onUpdateItemMediaPresentation,
2908
3090
  onUpdateItemSetting,
3091
+ onUploadItemMedia,
2909
3092
  searchQuery,
2910
- showInlineCopyFields
3093
+ showInlineCopyFields,
3094
+ uploadDisabled = false,
3095
+ isItemMediaUploading
2911
3096
  }) {
2912
3097
  const config = blockConfig[blockType];
2913
3098
  const normalizedQuery = searchQuery.trim().toLowerCase();
@@ -3057,11 +3242,30 @@ function ArrayItemsEditor({
3057
3242
  }
3058
3243
  )
3059
3244
  ] }) : null,
3060
- /* @__PURE__ */ jsxs11("div", { className: "orion-builder-settings-note", children: [
3061
- "Select this item and use the ",
3062
- /* @__PURE__ */ jsx12("strong", { children: "Media" }),
3063
- " group above for image source and presentation."
3064
- ] })
3245
+ /* @__PURE__ */ jsx12(
3246
+ ItemMediaControl,
3247
+ {
3248
+ fieldName: "media",
3249
+ imageURLFieldName: "imageURL",
3250
+ item,
3251
+ itemIndex,
3252
+ label: "Feature Image",
3253
+ maxHeight: 600,
3254
+ mediaLibrary,
3255
+ mediaLibraryError,
3256
+ mediaLibraryLoading,
3257
+ minHeight: 40,
3258
+ onRemoveItemMedia,
3259
+ onSelectItemMedia,
3260
+ onUpdateItemField,
3261
+ onUpdateItemMediaPresentation,
3262
+ onUploadItemMedia,
3263
+ searchQuery: normalizedQuery,
3264
+ uploadDisabled,
3265
+ uploadLabel: "Upload Feature Image",
3266
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3267
+ }
3268
+ )
3065
3269
  ] }) : null,
3066
3270
  blockType === "logoWall" ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
3067
3271
  showInlineCopyFields ? /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
@@ -3088,11 +3292,30 @@ function ArrayItemsEditor({
3088
3292
  }
3089
3293
  )
3090
3294
  ] }) : null,
3091
- /* @__PURE__ */ jsxs11("div", { className: "orion-builder-settings-note", children: [
3092
- "Select this item and use the ",
3093
- /* @__PURE__ */ jsx12("strong", { children: "Media" }),
3094
- " group above for image source and presentation."
3095
- ] })
3295
+ /* @__PURE__ */ jsx12(
3296
+ ItemMediaControl,
3297
+ {
3298
+ fieldName: "media",
3299
+ imageURLFieldName: "imageURL",
3300
+ item,
3301
+ itemIndex,
3302
+ label: "Logo Image",
3303
+ maxHeight: 200,
3304
+ mediaLibrary,
3305
+ mediaLibraryError,
3306
+ mediaLibraryLoading,
3307
+ minHeight: 24,
3308
+ onRemoveItemMedia,
3309
+ onSelectItemMedia,
3310
+ onUpdateItemField,
3311
+ onUpdateItemMediaPresentation,
3312
+ onUploadItemMedia,
3313
+ searchQuery: normalizedQuery,
3314
+ uploadDisabled,
3315
+ uploadLabel: "Upload Logo Image",
3316
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3317
+ }
3318
+ )
3096
3319
  ] }) : null,
3097
3320
  blockType === "beforeAfter" ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
3098
3321
  showInlineCopyFields ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
@@ -3120,11 +3343,54 @@ function ArrayItemsEditor({
3120
3343
  )
3121
3344
  ] })
3122
3345
  ] }) : null,
3123
- /* @__PURE__ */ jsxs11("div", { className: "orion-builder-settings-note", children: [
3124
- "Select this item and use the ",
3125
- /* @__PURE__ */ jsx12("strong", { children: "Media" }),
3126
- " group above for before/after image source and presentation."
3127
- ] })
3346
+ /* @__PURE__ */ jsx12(
3347
+ ItemMediaControl,
3348
+ {
3349
+ fieldName: "beforeMedia",
3350
+ imageURLFieldName: "beforeImageURL",
3351
+ item,
3352
+ itemIndex,
3353
+ label: "Before Image",
3354
+ maxHeight: 600,
3355
+ mediaLibrary,
3356
+ mediaLibraryError,
3357
+ mediaLibraryLoading,
3358
+ minHeight: 60,
3359
+ onRemoveItemMedia,
3360
+ onSelectItemMedia,
3361
+ onUpdateItemField,
3362
+ onUpdateItemMediaPresentation,
3363
+ onUploadItemMedia,
3364
+ searchQuery: normalizedQuery,
3365
+ uploadDisabled,
3366
+ uploadLabel: "Upload Before Image",
3367
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "beforeMedia"))
3368
+ }
3369
+ ),
3370
+ /* @__PURE__ */ jsx12(
3371
+ ItemMediaControl,
3372
+ {
3373
+ fieldName: "afterMedia",
3374
+ imageURLFieldName: "afterImageURL",
3375
+ item,
3376
+ itemIndex,
3377
+ label: "After Image",
3378
+ maxHeight: 600,
3379
+ mediaLibrary,
3380
+ mediaLibraryError,
3381
+ mediaLibraryLoading,
3382
+ minHeight: 60,
3383
+ onRemoveItemMedia,
3384
+ onSelectItemMedia,
3385
+ onUpdateItemField,
3386
+ onUpdateItemMediaPresentation,
3387
+ onUploadItemMedia,
3388
+ searchQuery: normalizedQuery,
3389
+ uploadDisabled,
3390
+ uploadLabel: "Upload After Image",
3391
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "afterMedia"))
3392
+ }
3393
+ )
3128
3394
  ] }) : null,
3129
3395
  blockType === "stats" ? /* @__PURE__ */ jsx12(Fragment4, { children: showInlineCopyFields ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
3130
3396
  /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
@@ -3330,8 +3596,8 @@ var resolveBuilderMediumHeroHeight = (topViewportHeight) => {
3330
3596
  }
3331
3597
  return "50svh";
3332
3598
  };
3333
- var normalizeImageFit = (value) => normalizeHeroImageFit(value);
3334
- var normalizeImageCornerStyle = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3599
+ var normalizeImageFit2 = (value) => normalizeHeroImageFit(value);
3600
+ var normalizeImageCornerStyle2 = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3335
3601
  var positionPercent = (value, fit) => {
3336
3602
  const resolved = fit === "cover" && (value === "left" || value === "right") ? "center" : value;
3337
3603
  switch (resolved) {
@@ -3862,7 +4128,7 @@ var sectionStyleFromBlock = (block, pageDefaults) => {
3862
4128
  sectionStyle: sectionMode === "color" ? { background: sectionColor } : sectionMode === "gradient" ? { background: sectionGradient } : block.blockType === "hero" ? { background: "transparent" } : {}
3863
4129
  };
3864
4130
  };
3865
- function getRelationID2(value) {
4131
+ function getRelationID3(value) {
3866
4132
  if (typeof value === "number" || typeof value === "string") {
3867
4133
  return value;
3868
4134
  }
@@ -3895,7 +4161,7 @@ function extractUploadedMedia(value) {
3895
4161
  if (!candidate || typeof candidate !== "object") {
3896
4162
  return null;
3897
4163
  }
3898
- const id = getRelationID2(candidate);
4164
+ const id = getRelationID3(candidate);
3899
4165
  if (id === null) {
3900
4166
  return null;
3901
4167
  }
@@ -3906,11 +4172,11 @@ function extractUploadedMedia(value) {
3906
4172
  url: typeof candidate.url === "string" ? candidate.url : ""
3907
4173
  };
3908
4174
  }
3909
- function toMediaLibraryItem2(value) {
4175
+ function toMediaLibraryItem3(value) {
3910
4176
  if (!value || typeof value !== "object") {
3911
4177
  return null;
3912
4178
  }
3913
- const id = getRelationID2(value);
4179
+ const id = getRelationID3(value);
3914
4180
  if (id === null) {
3915
4181
  return null;
3916
4182
  }
@@ -4088,7 +4354,7 @@ function BuilderPageEditor({
4088
4354
  }
4089
4355
  const json = await response.json();
4090
4356
  const docs = Array.isArray(json.docs) ? json.docs : [];
4091
- const items = docs.map((doc2) => toMediaLibraryItem2(doc2)).filter((item) => item !== null);
4357
+ const items = docs.map((doc2) => toMediaLibraryItem3(doc2)).filter((item) => item !== null);
4092
4358
  setMediaLibrary(items);
4093
4359
  } catch (error) {
4094
4360
  setMediaLibraryError(error instanceof Error ? error.message : "Could not load media library.");
@@ -4442,7 +4708,7 @@ function BuilderPageEditor({
4442
4708
  const nextLayout = cloneBlockLayout(layout);
4443
4709
  const block = nextLayout[selectedIndex];
4444
4710
  if (target.kind === "hero") {
4445
- const uploadedItem = toMediaLibraryItem2(uploaded);
4711
+ const uploadedItem = toMediaLibraryItem3(uploaded);
4446
4712
  nextLayout[selectedIndex] = {
4447
4713
  ...block,
4448
4714
  backgroundImageURL: uploadedItem?.url || normalizeText2(uploaded.url),
@@ -4506,13 +4772,13 @@ function BuilderPageEditor({
4506
4772
  const nextBlock = { ...migrateBlockToSettingsV2(block) };
4507
4773
  const blockType = normalizeText2(nextBlock.blockType);
4508
4774
  if (blockType === "hero") {
4509
- const mediaID = getRelationID2(nextBlock.media);
4775
+ const mediaID = getRelationID3(nextBlock.media);
4510
4776
  if (mediaID !== null) {
4511
4777
  nextBlock.media = mediaID;
4512
4778
  }
4513
4779
  }
4514
4780
  if (blockType === "media") {
4515
- const imageID = getRelationID2(nextBlock.image);
4781
+ const imageID = getRelationID3(nextBlock.image);
4516
4782
  if (imageID !== null) {
4517
4783
  nextBlock.image = imageID;
4518
4784
  }
@@ -4524,15 +4790,15 @@ function BuilderPageEditor({
4524
4790
  return rawItem;
4525
4791
  }
4526
4792
  const nextItem = { ...rawItem };
4527
- const mediaID = getRelationID2(nextItem.media);
4793
+ const mediaID = getRelationID3(nextItem.media);
4528
4794
  if (mediaID !== null) {
4529
4795
  nextItem.media = mediaID;
4530
4796
  }
4531
- const beforeMediaID = getRelationID2(nextItem.beforeMedia);
4797
+ const beforeMediaID = getRelationID3(nextItem.beforeMedia);
4532
4798
  if (beforeMediaID !== null) {
4533
4799
  nextItem.beforeMedia = beforeMediaID;
4534
4800
  }
4535
- const afterMediaID = getRelationID2(nextItem.afterMedia);
4801
+ const afterMediaID = getRelationID3(nextItem.afterMedia);
4536
4802
  if (afterMediaID !== null) {
4537
4803
  nextItem.afterMedia = afterMediaID;
4538
4804
  }
@@ -5110,8 +5376,8 @@ function BuilderPageEditor({
5110
5376
  }
5111
5377
  ] : [];
5112
5378
  const selectedMediaImageControls = selectedItemRecord && typeof selectedItemIndex === "number" && (selectedType === "featureGrid" || selectedType === "logoWall" || selectedType === "beforeAfter") ? {
5113
- cornerStyle: normalizeImageCornerStyle(selectedItemRecord.imageCornerStyle),
5114
- fit: normalizeImageFit(selectedItemRecord.imageFit),
5379
+ cornerStyle: normalizeImageCornerStyle2(selectedItemRecord.imageCornerStyle),
5380
+ fit: normalizeImageFit2(selectedItemRecord.imageFit),
5115
5381
  height: (() => {
5116
5382
  if (typeof selectedItemRecord.imageHeight === "number" && Number.isFinite(selectedItemRecord.imageHeight)) {
5117
5383
  return selectedItemRecord.imageHeight;
@@ -5586,8 +5852,8 @@ function BuilderPageEditor({
5586
5852
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5587
5853
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5588
5854
  const itemImageStyle = getImagePresentationStyle({
5589
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5590
- fit: normalizeImageFit(itemRecord?.imageFit),
5855
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5856
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5591
5857
  positionX: itemPositionX,
5592
5858
  positionY: itemPositionY
5593
5859
  });
@@ -5704,8 +5970,8 @@ function BuilderPageEditor({
5704
5970
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5705
5971
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5706
5972
  const itemImageStyle = getImagePresentationStyle({
5707
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5708
- fit: normalizeImageFit(itemRecord?.imageFit),
5973
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5974
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5709
5975
  positionX: itemPositionX,
5710
5976
  positionY: itemPositionY
5711
5977
  });
@@ -5911,8 +6177,8 @@ function BuilderPageEditor({
5911
6177
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5912
6178
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5913
6179
  const imageStyle = getImagePresentationStyle({
5914
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5915
- fit: normalizeImageFit(itemRecord?.imageFit),
6180
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6181
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5916
6182
  positionX: itemPositionX,
5917
6183
  positionY: itemPositionY,
5918
6184
  roundedRadius: 10
@@ -6025,8 +6291,8 @@ function BuilderPageEditor({
6025
6291
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
6026
6292
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
6027
6293
  const imageStyle = getImagePresentationStyle({
6028
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
6029
- fit: normalizeImageFit(itemRecord?.imageFit),
6294
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6295
+ fit: normalizeImageFit2(itemRecord?.imageFit),
6030
6296
  positionX: itemPositionX,
6031
6297
  positionY: itemPositionY
6032
6298
  });
@@ -6332,8 +6598,8 @@ function BuilderPageEditor({
6332
6598
  getImagePresentationStyle,
6333
6599
  index,
6334
6600
  isBlockUploadTarget,
6335
- normalizeImageCornerStyle,
6336
- normalizeImageFit,
6601
+ normalizeImageCornerStyle: normalizeImageCornerStyle2,
6602
+ normalizeImageFit: normalizeImageFit2,
6337
6603
  normalizeText: normalizeText2,
6338
6604
  onDropAt,
6339
6605
  renderWithSectionShell,
@@ -6562,14 +6828,23 @@ function BuilderPageEditor({
6562
6828
  blockType: selectedType,
6563
6829
  expandedItemIndex,
6564
6830
  items: selectedItems,
6831
+ isItemMediaUploading: isSelectedItemMediaUploading,
6832
+ mediaLibrary,
6833
+ mediaLibraryError,
6834
+ mediaLibraryLoading,
6565
6835
  mode: settingsPanelMode,
6566
6836
  onAddItem: appendDefaultItemToSelected,
6567
6837
  onRemoveItem: (itemIndex) => removeItemFromSelected("items", itemIndex),
6838
+ onRemoveItemMedia: (itemIndex, fieldName) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, ""),
6839
+ onSelectItemMedia: (itemIndex, fieldName, mediaID) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, mediaID),
6568
6840
  onToggleItem: toggleSelectedItem,
6569
6841
  onUpdateItemField: (itemIndex, fieldName, value) => updateArrayItemField(selectedIndex ?? 0, "items", itemIndex, fieldName, value),
6842
+ onUpdateItemMediaPresentation: (itemIndex, fieldName, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, `media.${fieldName}`, value),
6570
6843
  onUpdateItemSetting: (itemIndex, path, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, path, value),
6844
+ onUploadItemMedia: uploadItemMediaFromV2,
6571
6845
  searchQuery: settingsSearchQuery,
6572
- showInlineCopyFields: editCopyInPanelEnabled
6846
+ showInlineCopyFields: editCopyInPanelEnabled,
6847
+ uploadDisabled: uploadingTarget !== null
6573
6848
  }
6574
6849
  ) : null,
6575
6850
  /* @__PURE__ */ jsxs12("label", { style: sidebarLabelStyle, children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orion-studios/payload-studio",
3
- "version": "0.6.0-beta.22",
3
+ "version": "0.6.0-beta.24",
4
4
  "description": "Base CMS, builder, and custom admin toolkit for Orion Studios websites",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",