@orion-studios/payload-studio 0.6.0-beta.23 → 0.6.0-beta.25

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.
@@ -957,6 +957,12 @@ var toMediaLibraryItem = (value) => {
957
957
  };
958
958
  };
959
959
  var mediaLabel = (item) => item.filename || item.alt || `Media #${item.id}`;
960
+ var findMediaLibraryItem = (library, id) => {
961
+ if (id === null) {
962
+ return null;
963
+ }
964
+ return library.find((item) => String(item.id) === String(id)) || null;
965
+ };
960
966
  var groupLabel = (key) => commonInspectorGroups.find((group) => group.key === key)?.label || key;
961
967
  function BlockInspectorRenderer({
962
968
  block,
@@ -1080,8 +1086,9 @@ function BlockInspectorRenderer({
1080
1086
  title: group.label,
1081
1087
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-builder-settings-field-list", children: [
1082
1088
  group.key === "media" ? effectiveMediaSources.map((source) => {
1083
- const selectedSourceMedia = toMediaLibraryItem(source.value);
1084
1089
  const selectedSourceMediaID = getRelationID(source.value);
1090
+ const selectedRelationMedia = toMediaLibraryItem(source.value);
1091
+ const selectedSourceMedia = selectedRelationMedia ?? findMediaLibraryItem(source.library, selectedSourceMediaID);
1085
1092
  const sourceOptions = selectedSourceMedia && !source.library.some((item) => String(item.id) === String(selectedSourceMedia.id)) ? [selectedSourceMedia, ...source.library] : source.library;
1086
1093
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1087
1094
  "div",
@@ -1106,6 +1113,10 @@ function BlockInspectorRenderer({
1106
1113
  }
1107
1114
  )
1108
1115
  ] }),
1116
+ selectedSourceMedia?.alt ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1117
+ "Media Description",
1118
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "orion-builder-settings-input", readOnly: true, type: "text", value: selectedSourceMedia.alt })
1119
+ ] }) : selectedSourceMediaID !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-note", children: "This media item does not have a description yet." }) : null,
1109
1120
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1110
1121
  "button",
1111
1122
  {
@@ -2254,8 +2265,8 @@ function renderSimpleBlockPreview(args) {
2254
2265
  getImagePresentationStyle: getImagePresentationStyle2,
2255
2266
  index,
2256
2267
  isBlockUploadTarget,
2257
- normalizeImageCornerStyle: normalizeImageCornerStyle2,
2258
- normalizeImageFit: normalizeImageFit2,
2268
+ normalizeImageCornerStyle: normalizeImageCornerStyle3,
2269
+ normalizeImageFit: normalizeImageFit3,
2259
2270
  normalizeText: normalizeText3,
2260
2271
  onDropAt,
2261
2272
  renderWithSectionShell,
@@ -2439,8 +2450,8 @@ function renderSimpleBlockPreview(args) {
2439
2450
  const imagePositionX = parseOptionalPercentNumber(mediaSettings.positionX ?? block?.imagePositionX);
2440
2451
  const imagePositionY = parseOptionalPercentNumber(mediaSettings.positionY ?? block?.imagePositionY);
2441
2452
  const imageStyle = getImagePresentationStyle2({
2442
- cornerStyle: normalizeImageCornerStyle2(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2443
- fit: normalizeImageFit2(mediaSettings.fit ?? block?.imageFit),
2453
+ cornerStyle: normalizeImageCornerStyle3(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2454
+ fit: normalizeImageFit3(mediaSettings.fit ?? block?.imageFit),
2444
2455
  positionX: imagePositionX,
2445
2456
  positionY: imagePositionY
2446
2457
  });
@@ -2970,6 +2981,63 @@ var normalizeNumber2 = (value, fallback) => {
2970
2981
  return fallback;
2971
2982
  };
2972
2983
  var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2984
+ var getRelationID2 = (value) => {
2985
+ if (typeof value === "number" || typeof value === "string") {
2986
+ return value;
2987
+ }
2988
+ if (!isRecord5(value)) {
2989
+ return null;
2990
+ }
2991
+ const id = value.id;
2992
+ return typeof id === "number" || typeof id === "string" ? id : null;
2993
+ };
2994
+ var toMediaLibraryItem2 = (value) => {
2995
+ if (!isRecord5(value)) {
2996
+ return null;
2997
+ }
2998
+ const id = getRelationID2(value);
2999
+ if (id === null) {
3000
+ return null;
3001
+ }
3002
+ const filename = typeof value.filename === "string" ? value.filename : "";
3003
+ const url = typeof value.url === "string" && value.url.length > 0 ? value.url : filename ? `/api/media/file/${encodeURIComponent(filename)}` : "";
3004
+ return {
3005
+ alt: typeof value.alt === "string" ? value.alt : "",
3006
+ filename,
3007
+ id,
3008
+ url
3009
+ };
3010
+ };
3011
+ var mediaLabel2 = (item) => item.filename || item.alt || `Media #${item.id}`;
3012
+ var findMediaLibraryItem2 = (library, id) => {
3013
+ if (id === null) {
3014
+ return null;
3015
+ }
3016
+ return library.find((item) => String(item.id) === String(id)) || null;
3017
+ };
3018
+ var normalizeImageFit = (value) => value === "contain" ? "contain" : "cover";
3019
+ var normalizeImageCornerStyle = (value) => value === "square" ? "square" : "rounded";
3020
+ var getItemMediaSettings = (item) => {
3021
+ const settings = isRecord5(item.settings) ? item.settings : {};
3022
+ const media = isRecord5(settings.media) ? settings.media : {};
3023
+ return {
3024
+ cornerStyle: normalizeImageCornerStyle(media.cornerStyle ?? item.imageCornerStyle),
3025
+ fit: normalizeImageFit(media.fit ?? item.imageFit),
3026
+ height: (() => {
3027
+ const value = media.height ?? item.imageHeight;
3028
+ if (typeof value === "number" && Number.isFinite(value)) {
3029
+ return value;
3030
+ }
3031
+ if (typeof value === "string" && value.trim().length > 0) {
3032
+ const parsed = Number(value);
3033
+ return Number.isFinite(parsed) ? parsed : null;
3034
+ }
3035
+ return null;
3036
+ })(),
3037
+ positionX: clamp(normalizeNumber2(media.positionX ?? item.imagePositionX, 50), 0, 100),
3038
+ positionY: clamp(normalizeNumber2(media.positionY ?? item.imagePositionY, 50), 0, 100)
3039
+ };
3040
+ };
2973
3041
  var hasQueryMatch = (query, ...values) => {
2974
3042
  const normalized = query.trim().toLowerCase();
2975
3043
  if (!normalized) {
@@ -3016,18 +3084,157 @@ var bulletsToTextareaValue = (value) => {
3016
3084
  return value.map((item) => isRecord5(item) && typeof item.label === "string" ? item.label.trim() : "").filter(Boolean).join("\n");
3017
3085
  };
3018
3086
  var textareaValueToBullets = (value) => value.split("\n").map((line) => line.trim()).filter(Boolean).map((label) => ({ label }));
3087
+ function ItemMediaControl({
3088
+ fieldName,
3089
+ imageURLFieldName,
3090
+ item,
3091
+ itemIndex,
3092
+ label,
3093
+ mediaLibrary,
3094
+ mediaLibraryError,
3095
+ mediaLibraryLoading,
3096
+ maxHeight,
3097
+ minHeight,
3098
+ onRemoveItemMedia,
3099
+ onSelectItemMedia,
3100
+ onUpdateItemField,
3101
+ onUpdateItemMediaPresentation,
3102
+ onUploadItemMedia,
3103
+ searchQuery,
3104
+ uploadDisabled,
3105
+ uploadLabel,
3106
+ uploading
3107
+ }) {
3108
+ const selectedMediaID = getRelationID2(item[fieldName]);
3109
+ const selectedRelationMedia = toMediaLibraryItem2(item[fieldName]);
3110
+ const selectedMedia = selectedRelationMedia ?? findMediaLibraryItem2(mediaLibrary, selectedMediaID);
3111
+ const directImageURL = normalizeText(item[imageURLFieldName]);
3112
+ const previewURL = selectedMedia?.url || directImageURL;
3113
+ const mediaSettings = getItemMediaSettings(item);
3114
+ const sourceOptions = selectedMedia && !mediaLibrary.some((libraryItem) => String(libraryItem.id) === String(selectedMedia.id)) ? [selectedMedia, ...mediaLibrary] : mediaLibrary;
3115
+ if (!hasQueryMatch(searchQuery, label, "image", "media", "photo", "picture", "url", "fit", "crop", "height")) {
3116
+ return null;
3117
+ }
3118
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-item-card", style: { padding: "0.56rem" }, children: [
3119
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-label", children: label }),
3120
+ previewURL ? (
3121
+ // eslint-disable-next-line @next/next/no-img-element
3122
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3123
+ "img",
3124
+ {
3125
+ alt: selectedMedia?.alt || label,
3126
+ src: previewURL,
3127
+ style: {
3128
+ aspectRatio: "16 / 9",
3129
+ border: "1px solid rgba(35, 51, 82, 0.14)",
3130
+ borderRadius: mediaSettings.cornerStyle === "square" ? 4 : 10,
3131
+ display: "block",
3132
+ objectFit: mediaSettings.fit,
3133
+ objectPosition: `${mediaSettings.positionX}% ${mediaSettings.positionY}%`,
3134
+ width: "100%"
3135
+ }
3136
+ }
3137
+ )
3138
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-empty", children: "No image selected." }),
3139
+ mediaLibraryLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-note", children: "Loading media library..." }) : null,
3140
+ mediaLibraryError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-error", children: mediaLibraryError }) : null,
3141
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3142
+ "Media Library Image",
3143
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3144
+ "select",
3145
+ {
3146
+ className: "orion-builder-settings-input",
3147
+ onChange: (event) => onSelectItemMedia?.(itemIndex, fieldName, event.target.value),
3148
+ value: selectedMediaID !== null ? String(selectedMediaID) : "",
3149
+ children: [
3150
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: "", children: "No library image" }),
3151
+ sourceOptions.map((libraryItem) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: String(libraryItem.id), children: mediaLabel2(libraryItem) }, String(libraryItem.id)))
3152
+ ]
3153
+ }
3154
+ )
3155
+ ] }),
3156
+ selectedMedia?.alt ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3157
+ "Media Description",
3158
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { className: "orion-builder-settings-input", readOnly: true, type: "text", value: selectedMedia.alt })
3159
+ ] }) : selectedMediaID !== null ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-note", children: "This media item does not have a description yet." }) : null,
3160
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3161
+ "button",
3162
+ {
3163
+ className: "orion-builder-settings-inline-btn",
3164
+ disabled: selectedMediaID === null,
3165
+ onClick: () => onRemoveItemMedia?.(itemIndex, fieldName),
3166
+ type: "button",
3167
+ children: "Remove Library Image"
3168
+ }
3169
+ ),
3170
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3171
+ "Direct Image URL",
3172
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3173
+ "input",
3174
+ {
3175
+ className: "orion-builder-settings-input",
3176
+ onChange: (event) => onUpdateItemField(itemIndex, imageURLFieldName, event.target.value),
3177
+ type: "text",
3178
+ value: directImageURL
3179
+ }
3180
+ )
3181
+ ] }),
3182
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3183
+ uploadLabel,
3184
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3185
+ "input",
3186
+ {
3187
+ accept: "image/*",
3188
+ className: "orion-builder-settings-input",
3189
+ disabled: uploadDisabled,
3190
+ onChange: (event) => {
3191
+ const file = event.currentTarget.files?.[0];
3192
+ if (file) {
3193
+ onUploadItemMedia?.(itemIndex, fieldName, file);
3194
+ }
3195
+ event.currentTarget.value = "";
3196
+ },
3197
+ type: "file"
3198
+ }
3199
+ )
3200
+ ] }),
3201
+ uploading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-note", children: "Uploading image..." }) : null,
3202
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3203
+ ImageControls,
3204
+ {
3205
+ cornerStyle: mediaSettings.cornerStyle,
3206
+ fit: mediaSettings.fit,
3207
+ height: mediaSettings.height,
3208
+ maxHeight,
3209
+ minHeight,
3210
+ onChange: (field, value) => onUpdateItemMediaPresentation?.(itemIndex, field, value),
3211
+ positionX: mediaSettings.positionX,
3212
+ positionY: mediaSettings.positionY
3213
+ }
3214
+ )
3215
+ ] });
3216
+ }
3019
3217
  function ArrayItemsEditor({
3020
3218
  blockType,
3021
3219
  expandedItemIndex,
3022
3220
  items,
3221
+ mediaLibrary = [],
3222
+ mediaLibraryError = "",
3223
+ mediaLibraryLoading = false,
3023
3224
  mode,
3024
3225
  onAddItem,
3226
+ onRemoveItemMedia,
3025
3227
  onRemoveItem,
3228
+ onSelectItemMedia,
3026
3229
  onToggleItem,
3027
3230
  onUpdateItemField,
3231
+ onUpdateItemMediaPresentation,
3028
3232
  onUpdateItemSetting,
3233
+ onUploadItemMedia,
3029
3234
  searchQuery,
3030
- showInlineCopyFields
3235
+ showInlineCopyFields,
3236
+ uploadDisabled = false,
3237
+ isItemMediaUploading
3031
3238
  }) {
3032
3239
  const config = blockConfig[blockType];
3033
3240
  const normalizedQuery = searchQuery.trim().toLowerCase();
@@ -3177,11 +3384,30 @@ function ArrayItemsEditor({
3177
3384
  }
3178
3385
  )
3179
3386
  ] }) : 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
- ] })
3387
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3388
+ ItemMediaControl,
3389
+ {
3390
+ fieldName: "media",
3391
+ imageURLFieldName: "imageURL",
3392
+ item,
3393
+ itemIndex,
3394
+ label: "Feature Image",
3395
+ maxHeight: 600,
3396
+ mediaLibrary,
3397
+ mediaLibraryError,
3398
+ mediaLibraryLoading,
3399
+ minHeight: 40,
3400
+ onRemoveItemMedia,
3401
+ onSelectItemMedia,
3402
+ onUpdateItemField,
3403
+ onUpdateItemMediaPresentation,
3404
+ onUploadItemMedia,
3405
+ searchQuery: normalizedQuery,
3406
+ uploadDisabled,
3407
+ uploadLabel: "Upload Feature Image",
3408
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3409
+ }
3410
+ )
3185
3411
  ] }) : null,
3186
3412
  blockType === "logoWall" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3187
3413
  showInlineCopyFields ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
@@ -3208,11 +3434,30 @@ function ArrayItemsEditor({
3208
3434
  }
3209
3435
  )
3210
3436
  ] }) : 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
- ] })
3437
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3438
+ ItemMediaControl,
3439
+ {
3440
+ fieldName: "media",
3441
+ imageURLFieldName: "imageURL",
3442
+ item,
3443
+ itemIndex,
3444
+ label: "Logo Image",
3445
+ maxHeight: 200,
3446
+ mediaLibrary,
3447
+ mediaLibraryError,
3448
+ mediaLibraryLoading,
3449
+ minHeight: 24,
3450
+ onRemoveItemMedia,
3451
+ onSelectItemMedia,
3452
+ onUpdateItemField,
3453
+ onUpdateItemMediaPresentation,
3454
+ onUploadItemMedia,
3455
+ searchQuery: normalizedQuery,
3456
+ uploadDisabled,
3457
+ uploadLabel: "Upload Logo Image",
3458
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3459
+ }
3460
+ )
3216
3461
  ] }) : null,
3217
3462
  blockType === "beforeAfter" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3218
3463
  showInlineCopyFields ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
@@ -3240,11 +3485,54 @@ function ArrayItemsEditor({
3240
3485
  )
3241
3486
  ] })
3242
3487
  ] }) : 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
- ] })
3488
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3489
+ ItemMediaControl,
3490
+ {
3491
+ fieldName: "beforeMedia",
3492
+ imageURLFieldName: "beforeImageURL",
3493
+ item,
3494
+ itemIndex,
3495
+ label: "Before Image",
3496
+ maxHeight: 600,
3497
+ mediaLibrary,
3498
+ mediaLibraryError,
3499
+ mediaLibraryLoading,
3500
+ minHeight: 60,
3501
+ onRemoveItemMedia,
3502
+ onSelectItemMedia,
3503
+ onUpdateItemField,
3504
+ onUpdateItemMediaPresentation,
3505
+ onUploadItemMedia,
3506
+ searchQuery: normalizedQuery,
3507
+ uploadDisabled,
3508
+ uploadLabel: "Upload Before Image",
3509
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "beforeMedia"))
3510
+ }
3511
+ ),
3512
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3513
+ ItemMediaControl,
3514
+ {
3515
+ fieldName: "afterMedia",
3516
+ imageURLFieldName: "afterImageURL",
3517
+ item,
3518
+ itemIndex,
3519
+ label: "After Image",
3520
+ maxHeight: 600,
3521
+ mediaLibrary,
3522
+ mediaLibraryError,
3523
+ mediaLibraryLoading,
3524
+ minHeight: 60,
3525
+ onRemoveItemMedia,
3526
+ onSelectItemMedia,
3527
+ onUpdateItemField,
3528
+ onUpdateItemMediaPresentation,
3529
+ onUploadItemMedia,
3530
+ searchQuery: normalizedQuery,
3531
+ uploadDisabled,
3532
+ uploadLabel: "Upload After Image",
3533
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "afterMedia"))
3534
+ }
3535
+ )
3248
3536
  ] }) : null,
3249
3537
  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
3538
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
@@ -3450,8 +3738,8 @@ var resolveBuilderMediumHeroHeight = (topViewportHeight) => {
3450
3738
  }
3451
3739
  return "50svh";
3452
3740
  };
3453
- var normalizeImageFit = (value) => normalizeHeroImageFit(value);
3454
- var normalizeImageCornerStyle = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3741
+ var normalizeImageFit2 = (value) => normalizeHeroImageFit(value);
3742
+ var normalizeImageCornerStyle2 = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3455
3743
  var positionPercent = (value, fit) => {
3456
3744
  const resolved = fit === "cover" && (value === "left" || value === "right") ? "center" : value;
3457
3745
  switch (resolved) {
@@ -3982,7 +4270,7 @@ var sectionStyleFromBlock = (block, pageDefaults) => {
3982
4270
  sectionStyle: sectionMode === "color" ? { background: sectionColor } : sectionMode === "gradient" ? { background: sectionGradient } : block.blockType === "hero" ? { background: "transparent" } : {}
3983
4271
  };
3984
4272
  };
3985
- function getRelationID2(value) {
4273
+ function getRelationID3(value) {
3986
4274
  if (typeof value === "number" || typeof value === "string") {
3987
4275
  return value;
3988
4276
  }
@@ -4015,7 +4303,7 @@ function extractUploadedMedia(value) {
4015
4303
  if (!candidate || typeof candidate !== "object") {
4016
4304
  return null;
4017
4305
  }
4018
- const id = getRelationID2(candidate);
4306
+ const id = getRelationID3(candidate);
4019
4307
  if (id === null) {
4020
4308
  return null;
4021
4309
  }
@@ -4026,11 +4314,11 @@ function extractUploadedMedia(value) {
4026
4314
  url: typeof candidate.url === "string" ? candidate.url : ""
4027
4315
  };
4028
4316
  }
4029
- function toMediaLibraryItem2(value) {
4317
+ function toMediaLibraryItem3(value) {
4030
4318
  if (!value || typeof value !== "object") {
4031
4319
  return null;
4032
4320
  }
4033
- const id = getRelationID2(value);
4321
+ const id = getRelationID3(value);
4034
4322
  if (id === null) {
4035
4323
  return null;
4036
4324
  }
@@ -4208,7 +4496,7 @@ function BuilderPageEditor({
4208
4496
  }
4209
4497
  const json = await response.json();
4210
4498
  const docs = Array.isArray(json.docs) ? json.docs : [];
4211
- const items = docs.map((doc2) => toMediaLibraryItem2(doc2)).filter((item) => item !== null);
4499
+ const items = docs.map((doc2) => toMediaLibraryItem3(doc2)).filter((item) => item !== null);
4212
4500
  setMediaLibrary(items);
4213
4501
  } catch (error) {
4214
4502
  setMediaLibraryError(error instanceof Error ? error.message : "Could not load media library.");
@@ -4562,7 +4850,7 @@ function BuilderPageEditor({
4562
4850
  const nextLayout = cloneBlockLayout(layout);
4563
4851
  const block = nextLayout[selectedIndex];
4564
4852
  if (target.kind === "hero") {
4565
- const uploadedItem = toMediaLibraryItem2(uploaded);
4853
+ const uploadedItem = toMediaLibraryItem3(uploaded);
4566
4854
  nextLayout[selectedIndex] = {
4567
4855
  ...block,
4568
4856
  backgroundImageURL: uploadedItem?.url || normalizeText2(uploaded.url),
@@ -4626,13 +4914,13 @@ function BuilderPageEditor({
4626
4914
  const nextBlock = { ...migrateBlockToSettingsV2(block) };
4627
4915
  const blockType = normalizeText2(nextBlock.blockType);
4628
4916
  if (blockType === "hero") {
4629
- const mediaID = getRelationID2(nextBlock.media);
4917
+ const mediaID = getRelationID3(nextBlock.media);
4630
4918
  if (mediaID !== null) {
4631
4919
  nextBlock.media = mediaID;
4632
4920
  }
4633
4921
  }
4634
4922
  if (blockType === "media") {
4635
- const imageID = getRelationID2(nextBlock.image);
4923
+ const imageID = getRelationID3(nextBlock.image);
4636
4924
  if (imageID !== null) {
4637
4925
  nextBlock.image = imageID;
4638
4926
  }
@@ -4644,15 +4932,15 @@ function BuilderPageEditor({
4644
4932
  return rawItem;
4645
4933
  }
4646
4934
  const nextItem = { ...rawItem };
4647
- const mediaID = getRelationID2(nextItem.media);
4935
+ const mediaID = getRelationID3(nextItem.media);
4648
4936
  if (mediaID !== null) {
4649
4937
  nextItem.media = mediaID;
4650
4938
  }
4651
- const beforeMediaID = getRelationID2(nextItem.beforeMedia);
4939
+ const beforeMediaID = getRelationID3(nextItem.beforeMedia);
4652
4940
  if (beforeMediaID !== null) {
4653
4941
  nextItem.beforeMedia = beforeMediaID;
4654
4942
  }
4655
- const afterMediaID = getRelationID2(nextItem.afterMedia);
4943
+ const afterMediaID = getRelationID3(nextItem.afterMedia);
4656
4944
  if (afterMediaID !== null) {
4657
4945
  nextItem.afterMedia = afterMediaID;
4658
4946
  }
@@ -5230,8 +5518,8 @@ function BuilderPageEditor({
5230
5518
  }
5231
5519
  ] : [];
5232
5520
  const selectedMediaImageControls = selectedItemRecord && typeof selectedItemIndex === "number" && (selectedType === "featureGrid" || selectedType === "logoWall" || selectedType === "beforeAfter") ? {
5233
- cornerStyle: normalizeImageCornerStyle(selectedItemRecord.imageCornerStyle),
5234
- fit: normalizeImageFit(selectedItemRecord.imageFit),
5521
+ cornerStyle: normalizeImageCornerStyle2(selectedItemRecord.imageCornerStyle),
5522
+ fit: normalizeImageFit2(selectedItemRecord.imageFit),
5235
5523
  height: (() => {
5236
5524
  if (typeof selectedItemRecord.imageHeight === "number" && Number.isFinite(selectedItemRecord.imageHeight)) {
5237
5525
  return selectedItemRecord.imageHeight;
@@ -5706,8 +5994,8 @@ function BuilderPageEditor({
5706
5994
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5707
5995
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5708
5996
  const itemImageStyle = getImagePresentationStyle({
5709
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5710
- fit: normalizeImageFit(itemRecord?.imageFit),
5997
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5998
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5711
5999
  positionX: itemPositionX,
5712
6000
  positionY: itemPositionY
5713
6001
  });
@@ -5824,8 +6112,8 @@ function BuilderPageEditor({
5824
6112
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5825
6113
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5826
6114
  const itemImageStyle = getImagePresentationStyle({
5827
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5828
- fit: normalizeImageFit(itemRecord?.imageFit),
6115
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6116
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5829
6117
  positionX: itemPositionX,
5830
6118
  positionY: itemPositionY
5831
6119
  });
@@ -6031,8 +6319,8 @@ function BuilderPageEditor({
6031
6319
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
6032
6320
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
6033
6321
  const imageStyle = getImagePresentationStyle({
6034
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
6035
- fit: normalizeImageFit(itemRecord?.imageFit),
6322
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6323
+ fit: normalizeImageFit2(itemRecord?.imageFit),
6036
6324
  positionX: itemPositionX,
6037
6325
  positionY: itemPositionY,
6038
6326
  roundedRadius: 10
@@ -6145,8 +6433,8 @@ function BuilderPageEditor({
6145
6433
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
6146
6434
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
6147
6435
  const imageStyle = getImagePresentationStyle({
6148
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
6149
- fit: normalizeImageFit(itemRecord?.imageFit),
6436
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6437
+ fit: normalizeImageFit2(itemRecord?.imageFit),
6150
6438
  positionX: itemPositionX,
6151
6439
  positionY: itemPositionY
6152
6440
  });
@@ -6452,8 +6740,8 @@ function BuilderPageEditor({
6452
6740
  getImagePresentationStyle,
6453
6741
  index,
6454
6742
  isBlockUploadTarget,
6455
- normalizeImageCornerStyle,
6456
- normalizeImageFit,
6743
+ normalizeImageCornerStyle: normalizeImageCornerStyle2,
6744
+ normalizeImageFit: normalizeImageFit2,
6457
6745
  normalizeText: normalizeText2,
6458
6746
  onDropAt,
6459
6747
  renderWithSectionShell,
@@ -6682,23 +6970,32 @@ function BuilderPageEditor({
6682
6970
  blockType: selectedType,
6683
6971
  expandedItemIndex,
6684
6972
  items: selectedItems,
6973
+ isItemMediaUploading: isSelectedItemMediaUploading,
6974
+ mediaLibrary,
6975
+ mediaLibraryError,
6976
+ mediaLibraryLoading,
6685
6977
  mode: settingsPanelMode,
6686
6978
  onAddItem: appendDefaultItemToSelected,
6687
6979
  onRemoveItem: (itemIndex) => removeItemFromSelected("items", itemIndex),
6980
+ onRemoveItemMedia: (itemIndex, fieldName) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, ""),
6981
+ onSelectItemMedia: (itemIndex, fieldName, mediaID) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, mediaID),
6688
6982
  onToggleItem: toggleSelectedItem,
6689
6983
  onUpdateItemField: (itemIndex, fieldName, value) => updateArrayItemField(selectedIndex ?? 0, "items", itemIndex, fieldName, value),
6984
+ onUpdateItemMediaPresentation: (itemIndex, fieldName, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, `media.${fieldName}`, value),
6690
6985
  onUpdateItemSetting: (itemIndex, path, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, path, value),
6986
+ onUploadItemMedia: uploadItemMediaFromV2,
6691
6987
  searchQuery: settingsSearchQuery,
6692
- showInlineCopyFields: editCopyInPanelEnabled
6988
+ showInlineCopyFields: editCopyInPanelEnabled,
6989
+ uploadDisabled: uploadingTarget !== null
6693
6990
  }
6694
6991
  ) : null,
6695
6992
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { style: sidebarLabelStyle, children: [
6696
- "Upload Alt Text",
6993
+ "New Upload Alt Text",
6697
6994
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
6698
6995
  "input",
6699
6996
  {
6700
6997
  onChange: (event) => setUploadAltText(event.target.value),
6701
- placeholder: "Describe the image",
6998
+ placeholder: "Used only when uploading a new image",
6702
6999
  style: sidebarInputStyle,
6703
7000
  type: "text",
6704
7001
  value: uploadAltText
@@ -930,6 +930,12 @@ var toMediaLibraryItem = (value) => {
930
930
  };
931
931
  };
932
932
  var mediaLabel = (item) => item.filename || item.alt || `Media #${item.id}`;
933
+ var findMediaLibraryItem = (library, id) => {
934
+ if (id === null) {
935
+ return null;
936
+ }
937
+ return library.find((item) => String(item.id) === String(id)) || null;
938
+ };
933
939
  var groupLabel = (key) => commonInspectorGroups.find((group) => group.key === key)?.label || key;
934
940
  function BlockInspectorRenderer({
935
941
  block,
@@ -1053,8 +1059,9 @@ function BlockInspectorRenderer({
1053
1059
  title: group.label,
1054
1060
  children: /* @__PURE__ */ jsxs3("div", { className: "orion-builder-settings-field-list", children: [
1055
1061
  group.key === "media" ? effectiveMediaSources.map((source) => {
1056
- const selectedSourceMedia = toMediaLibraryItem(source.value);
1057
1062
  const selectedSourceMediaID = getRelationID(source.value);
1063
+ const selectedRelationMedia = toMediaLibraryItem(source.value);
1064
+ const selectedSourceMedia = selectedRelationMedia ?? findMediaLibraryItem(source.library, selectedSourceMediaID);
1058
1065
  const sourceOptions = selectedSourceMedia && !source.library.some((item) => String(item.id) === String(selectedSourceMedia.id)) ? [selectedSourceMedia, ...source.library] : source.library;
1059
1066
  return /* @__PURE__ */ jsxs3(
1060
1067
  "div",
@@ -1079,6 +1086,10 @@ function BlockInspectorRenderer({
1079
1086
  }
1080
1087
  )
1081
1088
  ] }),
1089
+ selectedSourceMedia?.alt ? /* @__PURE__ */ jsxs3("label", { className: "orion-builder-settings-label", children: [
1090
+ "Media Description",
1091
+ /* @__PURE__ */ jsx3("input", { className: "orion-builder-settings-input", readOnly: true, type: "text", value: selectedSourceMedia.alt })
1092
+ ] }) : selectedSourceMediaID !== null ? /* @__PURE__ */ jsx3("div", { className: "orion-builder-settings-note", children: "This media item does not have a description yet." }) : null,
1082
1093
  /* @__PURE__ */ jsx3(
1083
1094
  "button",
1084
1095
  {
@@ -2134,8 +2145,8 @@ function renderSimpleBlockPreview(args) {
2134
2145
  getImagePresentationStyle: getImagePresentationStyle2,
2135
2146
  index,
2136
2147
  isBlockUploadTarget,
2137
- normalizeImageCornerStyle: normalizeImageCornerStyle2,
2138
- normalizeImageFit: normalizeImageFit2,
2148
+ normalizeImageCornerStyle: normalizeImageCornerStyle3,
2149
+ normalizeImageFit: normalizeImageFit3,
2139
2150
  normalizeText: normalizeText3,
2140
2151
  onDropAt,
2141
2152
  renderWithSectionShell,
@@ -2319,8 +2330,8 @@ function renderSimpleBlockPreview(args) {
2319
2330
  const imagePositionX = parseOptionalPercentNumber(mediaSettings.positionX ?? block?.imagePositionX);
2320
2331
  const imagePositionY = parseOptionalPercentNumber(mediaSettings.positionY ?? block?.imagePositionY);
2321
2332
  const imageStyle = getImagePresentationStyle2({
2322
- cornerStyle: normalizeImageCornerStyle2(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2323
- fit: normalizeImageFit2(mediaSettings.fit ?? block?.imageFit),
2333
+ cornerStyle: normalizeImageCornerStyle3(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2334
+ fit: normalizeImageFit3(mediaSettings.fit ?? block?.imageFit),
2324
2335
  positionX: imagePositionX,
2325
2336
  positionY: imagePositionY
2326
2337
  });
@@ -2850,6 +2861,63 @@ var normalizeNumber2 = (value, fallback) => {
2850
2861
  return fallback;
2851
2862
  };
2852
2863
  var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2864
+ var getRelationID2 = (value) => {
2865
+ if (typeof value === "number" || typeof value === "string") {
2866
+ return value;
2867
+ }
2868
+ if (!isRecord5(value)) {
2869
+ return null;
2870
+ }
2871
+ const id = value.id;
2872
+ return typeof id === "number" || typeof id === "string" ? id : null;
2873
+ };
2874
+ var toMediaLibraryItem2 = (value) => {
2875
+ if (!isRecord5(value)) {
2876
+ return null;
2877
+ }
2878
+ const id = getRelationID2(value);
2879
+ if (id === null) {
2880
+ return null;
2881
+ }
2882
+ const filename = typeof value.filename === "string" ? value.filename : "";
2883
+ const url = typeof value.url === "string" && value.url.length > 0 ? value.url : filename ? `/api/media/file/${encodeURIComponent(filename)}` : "";
2884
+ return {
2885
+ alt: typeof value.alt === "string" ? value.alt : "",
2886
+ filename,
2887
+ id,
2888
+ url
2889
+ };
2890
+ };
2891
+ var mediaLabel2 = (item) => item.filename || item.alt || `Media #${item.id}`;
2892
+ var findMediaLibraryItem2 = (library, id) => {
2893
+ if (id === null) {
2894
+ return null;
2895
+ }
2896
+ return library.find((item) => String(item.id) === String(id)) || null;
2897
+ };
2898
+ var normalizeImageFit = (value) => value === "contain" ? "contain" : "cover";
2899
+ var normalizeImageCornerStyle = (value) => value === "square" ? "square" : "rounded";
2900
+ var getItemMediaSettings = (item) => {
2901
+ const settings = isRecord5(item.settings) ? item.settings : {};
2902
+ const media = isRecord5(settings.media) ? settings.media : {};
2903
+ return {
2904
+ cornerStyle: normalizeImageCornerStyle(media.cornerStyle ?? item.imageCornerStyle),
2905
+ fit: normalizeImageFit(media.fit ?? item.imageFit),
2906
+ height: (() => {
2907
+ const value = media.height ?? item.imageHeight;
2908
+ if (typeof value === "number" && Number.isFinite(value)) {
2909
+ return value;
2910
+ }
2911
+ if (typeof value === "string" && value.trim().length > 0) {
2912
+ const parsed = Number(value);
2913
+ return Number.isFinite(parsed) ? parsed : null;
2914
+ }
2915
+ return null;
2916
+ })(),
2917
+ positionX: clamp(normalizeNumber2(media.positionX ?? item.imagePositionX, 50), 0, 100),
2918
+ positionY: clamp(normalizeNumber2(media.positionY ?? item.imagePositionY, 50), 0, 100)
2919
+ };
2920
+ };
2853
2921
  var hasQueryMatch = (query, ...values) => {
2854
2922
  const normalized = query.trim().toLowerCase();
2855
2923
  if (!normalized) {
@@ -2896,18 +2964,157 @@ var bulletsToTextareaValue = (value) => {
2896
2964
  return value.map((item) => isRecord5(item) && typeof item.label === "string" ? item.label.trim() : "").filter(Boolean).join("\n");
2897
2965
  };
2898
2966
  var textareaValueToBullets = (value) => value.split("\n").map((line) => line.trim()).filter(Boolean).map((label) => ({ label }));
2967
+ function ItemMediaControl({
2968
+ fieldName,
2969
+ imageURLFieldName,
2970
+ item,
2971
+ itemIndex,
2972
+ label,
2973
+ mediaLibrary,
2974
+ mediaLibraryError,
2975
+ mediaLibraryLoading,
2976
+ maxHeight,
2977
+ minHeight,
2978
+ onRemoveItemMedia,
2979
+ onSelectItemMedia,
2980
+ onUpdateItemField,
2981
+ onUpdateItemMediaPresentation,
2982
+ onUploadItemMedia,
2983
+ searchQuery,
2984
+ uploadDisabled,
2985
+ uploadLabel,
2986
+ uploading
2987
+ }) {
2988
+ const selectedMediaID = getRelationID2(item[fieldName]);
2989
+ const selectedRelationMedia = toMediaLibraryItem2(item[fieldName]);
2990
+ const selectedMedia = selectedRelationMedia ?? findMediaLibraryItem2(mediaLibrary, selectedMediaID);
2991
+ const directImageURL = normalizeText(item[imageURLFieldName]);
2992
+ const previewURL = selectedMedia?.url || directImageURL;
2993
+ const mediaSettings = getItemMediaSettings(item);
2994
+ const sourceOptions = selectedMedia && !mediaLibrary.some((libraryItem) => String(libraryItem.id) === String(selectedMedia.id)) ? [selectedMedia, ...mediaLibrary] : mediaLibrary;
2995
+ if (!hasQueryMatch(searchQuery, label, "image", "media", "photo", "picture", "url", "fit", "crop", "height")) {
2996
+ return null;
2997
+ }
2998
+ return /* @__PURE__ */ jsxs11("div", { className: "orion-builder-settings-item-card", style: { padding: "0.56rem" }, children: [
2999
+ /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-label", children: label }),
3000
+ previewURL ? (
3001
+ // eslint-disable-next-line @next/next/no-img-element
3002
+ /* @__PURE__ */ jsx12(
3003
+ "img",
3004
+ {
3005
+ alt: selectedMedia?.alt || label,
3006
+ src: previewURL,
3007
+ style: {
3008
+ aspectRatio: "16 / 9",
3009
+ border: "1px solid rgba(35, 51, 82, 0.14)",
3010
+ borderRadius: mediaSettings.cornerStyle === "square" ? 4 : 10,
3011
+ display: "block",
3012
+ objectFit: mediaSettings.fit,
3013
+ objectPosition: `${mediaSettings.positionX}% ${mediaSettings.positionY}%`,
3014
+ width: "100%"
3015
+ }
3016
+ }
3017
+ )
3018
+ ) : /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-empty", children: "No image selected." }),
3019
+ mediaLibraryLoading ? /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-note", children: "Loading media library..." }) : null,
3020
+ mediaLibraryError ? /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-error", children: mediaLibraryError }) : null,
3021
+ /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
3022
+ "Media Library Image",
3023
+ /* @__PURE__ */ jsxs11(
3024
+ "select",
3025
+ {
3026
+ className: "orion-builder-settings-input",
3027
+ onChange: (event) => onSelectItemMedia?.(itemIndex, fieldName, event.target.value),
3028
+ value: selectedMediaID !== null ? String(selectedMediaID) : "",
3029
+ children: [
3030
+ /* @__PURE__ */ jsx12("option", { value: "", children: "No library image" }),
3031
+ sourceOptions.map((libraryItem) => /* @__PURE__ */ jsx12("option", { value: String(libraryItem.id), children: mediaLabel2(libraryItem) }, String(libraryItem.id)))
3032
+ ]
3033
+ }
3034
+ )
3035
+ ] }),
3036
+ selectedMedia?.alt ? /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
3037
+ "Media Description",
3038
+ /* @__PURE__ */ jsx12("input", { className: "orion-builder-settings-input", readOnly: true, type: "text", value: selectedMedia.alt })
3039
+ ] }) : selectedMediaID !== null ? /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-note", children: "This media item does not have a description yet." }) : null,
3040
+ /* @__PURE__ */ jsx12(
3041
+ "button",
3042
+ {
3043
+ className: "orion-builder-settings-inline-btn",
3044
+ disabled: selectedMediaID === null,
3045
+ onClick: () => onRemoveItemMedia?.(itemIndex, fieldName),
3046
+ type: "button",
3047
+ children: "Remove Library Image"
3048
+ }
3049
+ ),
3050
+ /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
3051
+ "Direct Image URL",
3052
+ /* @__PURE__ */ jsx12(
3053
+ "input",
3054
+ {
3055
+ className: "orion-builder-settings-input",
3056
+ onChange: (event) => onUpdateItemField(itemIndex, imageURLFieldName, event.target.value),
3057
+ type: "text",
3058
+ value: directImageURL
3059
+ }
3060
+ )
3061
+ ] }),
3062
+ /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
3063
+ uploadLabel,
3064
+ /* @__PURE__ */ jsx12(
3065
+ "input",
3066
+ {
3067
+ accept: "image/*",
3068
+ className: "orion-builder-settings-input",
3069
+ disabled: uploadDisabled,
3070
+ onChange: (event) => {
3071
+ const file = event.currentTarget.files?.[0];
3072
+ if (file) {
3073
+ onUploadItemMedia?.(itemIndex, fieldName, file);
3074
+ }
3075
+ event.currentTarget.value = "";
3076
+ },
3077
+ type: "file"
3078
+ }
3079
+ )
3080
+ ] }),
3081
+ uploading ? /* @__PURE__ */ jsx12("div", { className: "orion-builder-settings-note", children: "Uploading image..." }) : null,
3082
+ /* @__PURE__ */ jsx12(
3083
+ ImageControls,
3084
+ {
3085
+ cornerStyle: mediaSettings.cornerStyle,
3086
+ fit: mediaSettings.fit,
3087
+ height: mediaSettings.height,
3088
+ maxHeight,
3089
+ minHeight,
3090
+ onChange: (field, value) => onUpdateItemMediaPresentation?.(itemIndex, field, value),
3091
+ positionX: mediaSettings.positionX,
3092
+ positionY: mediaSettings.positionY
3093
+ }
3094
+ )
3095
+ ] });
3096
+ }
2899
3097
  function ArrayItemsEditor({
2900
3098
  blockType,
2901
3099
  expandedItemIndex,
2902
3100
  items,
3101
+ mediaLibrary = [],
3102
+ mediaLibraryError = "",
3103
+ mediaLibraryLoading = false,
2903
3104
  mode,
2904
3105
  onAddItem,
3106
+ onRemoveItemMedia,
2905
3107
  onRemoveItem,
3108
+ onSelectItemMedia,
2906
3109
  onToggleItem,
2907
3110
  onUpdateItemField,
3111
+ onUpdateItemMediaPresentation,
2908
3112
  onUpdateItemSetting,
3113
+ onUploadItemMedia,
2909
3114
  searchQuery,
2910
- showInlineCopyFields
3115
+ showInlineCopyFields,
3116
+ uploadDisabled = false,
3117
+ isItemMediaUploading
2911
3118
  }) {
2912
3119
  const config = blockConfig[blockType];
2913
3120
  const normalizedQuery = searchQuery.trim().toLowerCase();
@@ -3057,11 +3264,30 @@ function ArrayItemsEditor({
3057
3264
  }
3058
3265
  )
3059
3266
  ] }) : 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
- ] })
3267
+ /* @__PURE__ */ jsx12(
3268
+ ItemMediaControl,
3269
+ {
3270
+ fieldName: "media",
3271
+ imageURLFieldName: "imageURL",
3272
+ item,
3273
+ itemIndex,
3274
+ label: "Feature Image",
3275
+ maxHeight: 600,
3276
+ mediaLibrary,
3277
+ mediaLibraryError,
3278
+ mediaLibraryLoading,
3279
+ minHeight: 40,
3280
+ onRemoveItemMedia,
3281
+ onSelectItemMedia,
3282
+ onUpdateItemField,
3283
+ onUpdateItemMediaPresentation,
3284
+ onUploadItemMedia,
3285
+ searchQuery: normalizedQuery,
3286
+ uploadDisabled,
3287
+ uploadLabel: "Upload Feature Image",
3288
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3289
+ }
3290
+ )
3065
3291
  ] }) : null,
3066
3292
  blockType === "logoWall" ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
3067
3293
  showInlineCopyFields ? /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
@@ -3088,11 +3314,30 @@ function ArrayItemsEditor({
3088
3314
  }
3089
3315
  )
3090
3316
  ] }) : 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
- ] })
3317
+ /* @__PURE__ */ jsx12(
3318
+ ItemMediaControl,
3319
+ {
3320
+ fieldName: "media",
3321
+ imageURLFieldName: "imageURL",
3322
+ item,
3323
+ itemIndex,
3324
+ label: "Logo Image",
3325
+ maxHeight: 200,
3326
+ mediaLibrary,
3327
+ mediaLibraryError,
3328
+ mediaLibraryLoading,
3329
+ minHeight: 24,
3330
+ onRemoveItemMedia,
3331
+ onSelectItemMedia,
3332
+ onUpdateItemField,
3333
+ onUpdateItemMediaPresentation,
3334
+ onUploadItemMedia,
3335
+ searchQuery: normalizedQuery,
3336
+ uploadDisabled,
3337
+ uploadLabel: "Upload Logo Image",
3338
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3339
+ }
3340
+ )
3096
3341
  ] }) : null,
3097
3342
  blockType === "beforeAfter" ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
3098
3343
  showInlineCopyFields ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
@@ -3120,11 +3365,54 @@ function ArrayItemsEditor({
3120
3365
  )
3121
3366
  ] })
3122
3367
  ] }) : 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
- ] })
3368
+ /* @__PURE__ */ jsx12(
3369
+ ItemMediaControl,
3370
+ {
3371
+ fieldName: "beforeMedia",
3372
+ imageURLFieldName: "beforeImageURL",
3373
+ item,
3374
+ itemIndex,
3375
+ label: "Before Image",
3376
+ maxHeight: 600,
3377
+ mediaLibrary,
3378
+ mediaLibraryError,
3379
+ mediaLibraryLoading,
3380
+ minHeight: 60,
3381
+ onRemoveItemMedia,
3382
+ onSelectItemMedia,
3383
+ onUpdateItemField,
3384
+ onUpdateItemMediaPresentation,
3385
+ onUploadItemMedia,
3386
+ searchQuery: normalizedQuery,
3387
+ uploadDisabled,
3388
+ uploadLabel: "Upload Before Image",
3389
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "beforeMedia"))
3390
+ }
3391
+ ),
3392
+ /* @__PURE__ */ jsx12(
3393
+ ItemMediaControl,
3394
+ {
3395
+ fieldName: "afterMedia",
3396
+ imageURLFieldName: "afterImageURL",
3397
+ item,
3398
+ itemIndex,
3399
+ label: "After Image",
3400
+ maxHeight: 600,
3401
+ mediaLibrary,
3402
+ mediaLibraryError,
3403
+ mediaLibraryLoading,
3404
+ minHeight: 60,
3405
+ onRemoveItemMedia,
3406
+ onSelectItemMedia,
3407
+ onUpdateItemField,
3408
+ onUpdateItemMediaPresentation,
3409
+ onUploadItemMedia,
3410
+ searchQuery: normalizedQuery,
3411
+ uploadDisabled,
3412
+ uploadLabel: "Upload After Image",
3413
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "afterMedia"))
3414
+ }
3415
+ )
3128
3416
  ] }) : null,
3129
3417
  blockType === "stats" ? /* @__PURE__ */ jsx12(Fragment4, { children: showInlineCopyFields ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
3130
3418
  /* @__PURE__ */ jsxs11("label", { className: "orion-builder-settings-label", children: [
@@ -3330,8 +3618,8 @@ var resolveBuilderMediumHeroHeight = (topViewportHeight) => {
3330
3618
  }
3331
3619
  return "50svh";
3332
3620
  };
3333
- var normalizeImageFit = (value) => normalizeHeroImageFit(value);
3334
- var normalizeImageCornerStyle = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3621
+ var normalizeImageFit2 = (value) => normalizeHeroImageFit(value);
3622
+ var normalizeImageCornerStyle2 = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3335
3623
  var positionPercent = (value, fit) => {
3336
3624
  const resolved = fit === "cover" && (value === "left" || value === "right") ? "center" : value;
3337
3625
  switch (resolved) {
@@ -3862,7 +4150,7 @@ var sectionStyleFromBlock = (block, pageDefaults) => {
3862
4150
  sectionStyle: sectionMode === "color" ? { background: sectionColor } : sectionMode === "gradient" ? { background: sectionGradient } : block.blockType === "hero" ? { background: "transparent" } : {}
3863
4151
  };
3864
4152
  };
3865
- function getRelationID2(value) {
4153
+ function getRelationID3(value) {
3866
4154
  if (typeof value === "number" || typeof value === "string") {
3867
4155
  return value;
3868
4156
  }
@@ -3895,7 +4183,7 @@ function extractUploadedMedia(value) {
3895
4183
  if (!candidate || typeof candidate !== "object") {
3896
4184
  return null;
3897
4185
  }
3898
- const id = getRelationID2(candidate);
4186
+ const id = getRelationID3(candidate);
3899
4187
  if (id === null) {
3900
4188
  return null;
3901
4189
  }
@@ -3906,11 +4194,11 @@ function extractUploadedMedia(value) {
3906
4194
  url: typeof candidate.url === "string" ? candidate.url : ""
3907
4195
  };
3908
4196
  }
3909
- function toMediaLibraryItem2(value) {
4197
+ function toMediaLibraryItem3(value) {
3910
4198
  if (!value || typeof value !== "object") {
3911
4199
  return null;
3912
4200
  }
3913
- const id = getRelationID2(value);
4201
+ const id = getRelationID3(value);
3914
4202
  if (id === null) {
3915
4203
  return null;
3916
4204
  }
@@ -4088,7 +4376,7 @@ function BuilderPageEditor({
4088
4376
  }
4089
4377
  const json = await response.json();
4090
4378
  const docs = Array.isArray(json.docs) ? json.docs : [];
4091
- const items = docs.map((doc2) => toMediaLibraryItem2(doc2)).filter((item) => item !== null);
4379
+ const items = docs.map((doc2) => toMediaLibraryItem3(doc2)).filter((item) => item !== null);
4092
4380
  setMediaLibrary(items);
4093
4381
  } catch (error) {
4094
4382
  setMediaLibraryError(error instanceof Error ? error.message : "Could not load media library.");
@@ -4442,7 +4730,7 @@ function BuilderPageEditor({
4442
4730
  const nextLayout = cloneBlockLayout(layout);
4443
4731
  const block = nextLayout[selectedIndex];
4444
4732
  if (target.kind === "hero") {
4445
- const uploadedItem = toMediaLibraryItem2(uploaded);
4733
+ const uploadedItem = toMediaLibraryItem3(uploaded);
4446
4734
  nextLayout[selectedIndex] = {
4447
4735
  ...block,
4448
4736
  backgroundImageURL: uploadedItem?.url || normalizeText2(uploaded.url),
@@ -4506,13 +4794,13 @@ function BuilderPageEditor({
4506
4794
  const nextBlock = { ...migrateBlockToSettingsV2(block) };
4507
4795
  const blockType = normalizeText2(nextBlock.blockType);
4508
4796
  if (blockType === "hero") {
4509
- const mediaID = getRelationID2(nextBlock.media);
4797
+ const mediaID = getRelationID3(nextBlock.media);
4510
4798
  if (mediaID !== null) {
4511
4799
  nextBlock.media = mediaID;
4512
4800
  }
4513
4801
  }
4514
4802
  if (blockType === "media") {
4515
- const imageID = getRelationID2(nextBlock.image);
4803
+ const imageID = getRelationID3(nextBlock.image);
4516
4804
  if (imageID !== null) {
4517
4805
  nextBlock.image = imageID;
4518
4806
  }
@@ -4524,15 +4812,15 @@ function BuilderPageEditor({
4524
4812
  return rawItem;
4525
4813
  }
4526
4814
  const nextItem = { ...rawItem };
4527
- const mediaID = getRelationID2(nextItem.media);
4815
+ const mediaID = getRelationID3(nextItem.media);
4528
4816
  if (mediaID !== null) {
4529
4817
  nextItem.media = mediaID;
4530
4818
  }
4531
- const beforeMediaID = getRelationID2(nextItem.beforeMedia);
4819
+ const beforeMediaID = getRelationID3(nextItem.beforeMedia);
4532
4820
  if (beforeMediaID !== null) {
4533
4821
  nextItem.beforeMedia = beforeMediaID;
4534
4822
  }
4535
- const afterMediaID = getRelationID2(nextItem.afterMedia);
4823
+ const afterMediaID = getRelationID3(nextItem.afterMedia);
4536
4824
  if (afterMediaID !== null) {
4537
4825
  nextItem.afterMedia = afterMediaID;
4538
4826
  }
@@ -5110,8 +5398,8 @@ function BuilderPageEditor({
5110
5398
  }
5111
5399
  ] : [];
5112
5400
  const selectedMediaImageControls = selectedItemRecord && typeof selectedItemIndex === "number" && (selectedType === "featureGrid" || selectedType === "logoWall" || selectedType === "beforeAfter") ? {
5113
- cornerStyle: normalizeImageCornerStyle(selectedItemRecord.imageCornerStyle),
5114
- fit: normalizeImageFit(selectedItemRecord.imageFit),
5401
+ cornerStyle: normalizeImageCornerStyle2(selectedItemRecord.imageCornerStyle),
5402
+ fit: normalizeImageFit2(selectedItemRecord.imageFit),
5115
5403
  height: (() => {
5116
5404
  if (typeof selectedItemRecord.imageHeight === "number" && Number.isFinite(selectedItemRecord.imageHeight)) {
5117
5405
  return selectedItemRecord.imageHeight;
@@ -5586,8 +5874,8 @@ function BuilderPageEditor({
5586
5874
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5587
5875
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5588
5876
  const itemImageStyle = getImagePresentationStyle({
5589
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5590
- fit: normalizeImageFit(itemRecord?.imageFit),
5877
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5878
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5591
5879
  positionX: itemPositionX,
5592
5880
  positionY: itemPositionY
5593
5881
  });
@@ -5704,8 +5992,8 @@ function BuilderPageEditor({
5704
5992
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5705
5993
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5706
5994
  const itemImageStyle = getImagePresentationStyle({
5707
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5708
- fit: normalizeImageFit(itemRecord?.imageFit),
5995
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5996
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5709
5997
  positionX: itemPositionX,
5710
5998
  positionY: itemPositionY
5711
5999
  });
@@ -5911,8 +6199,8 @@ function BuilderPageEditor({
5911
6199
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5912
6200
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5913
6201
  const imageStyle = getImagePresentationStyle({
5914
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5915
- fit: normalizeImageFit(itemRecord?.imageFit),
6202
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6203
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5916
6204
  positionX: itemPositionX,
5917
6205
  positionY: itemPositionY,
5918
6206
  roundedRadius: 10
@@ -6025,8 +6313,8 @@ function BuilderPageEditor({
6025
6313
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
6026
6314
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
6027
6315
  const imageStyle = getImagePresentationStyle({
6028
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
6029
- fit: normalizeImageFit(itemRecord?.imageFit),
6316
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6317
+ fit: normalizeImageFit2(itemRecord?.imageFit),
6030
6318
  positionX: itemPositionX,
6031
6319
  positionY: itemPositionY
6032
6320
  });
@@ -6332,8 +6620,8 @@ function BuilderPageEditor({
6332
6620
  getImagePresentationStyle,
6333
6621
  index,
6334
6622
  isBlockUploadTarget,
6335
- normalizeImageCornerStyle,
6336
- normalizeImageFit,
6623
+ normalizeImageCornerStyle: normalizeImageCornerStyle2,
6624
+ normalizeImageFit: normalizeImageFit2,
6337
6625
  normalizeText: normalizeText2,
6338
6626
  onDropAt,
6339
6627
  renderWithSectionShell,
@@ -6562,23 +6850,32 @@ function BuilderPageEditor({
6562
6850
  blockType: selectedType,
6563
6851
  expandedItemIndex,
6564
6852
  items: selectedItems,
6853
+ isItemMediaUploading: isSelectedItemMediaUploading,
6854
+ mediaLibrary,
6855
+ mediaLibraryError,
6856
+ mediaLibraryLoading,
6565
6857
  mode: settingsPanelMode,
6566
6858
  onAddItem: appendDefaultItemToSelected,
6567
6859
  onRemoveItem: (itemIndex) => removeItemFromSelected("items", itemIndex),
6860
+ onRemoveItemMedia: (itemIndex, fieldName) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, ""),
6861
+ onSelectItemMedia: (itemIndex, fieldName, mediaID) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, mediaID),
6568
6862
  onToggleItem: toggleSelectedItem,
6569
6863
  onUpdateItemField: (itemIndex, fieldName, value) => updateArrayItemField(selectedIndex ?? 0, "items", itemIndex, fieldName, value),
6864
+ onUpdateItemMediaPresentation: (itemIndex, fieldName, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, `media.${fieldName}`, value),
6570
6865
  onUpdateItemSetting: (itemIndex, path, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, path, value),
6866
+ onUploadItemMedia: uploadItemMediaFromV2,
6571
6867
  searchQuery: settingsSearchQuery,
6572
- showInlineCopyFields: editCopyInPanelEnabled
6868
+ showInlineCopyFields: editCopyInPanelEnabled,
6869
+ uploadDisabled: uploadingTarget !== null
6573
6870
  }
6574
6871
  ) : null,
6575
6872
  /* @__PURE__ */ jsxs12("label", { style: sidebarLabelStyle, children: [
6576
- "Upload Alt Text",
6873
+ "New Upload Alt Text",
6577
6874
  /* @__PURE__ */ jsx13(
6578
6875
  "input",
6579
6876
  {
6580
6877
  onChange: (event) => setUploadAltText(event.target.value),
6581
- placeholder: "Describe the image",
6878
+ placeholder: "Used only when uploading a new image",
6582
6879
  style: sidebarInputStyle,
6583
6880
  type: "text",
6584
6881
  value: uploadAltText
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orion-studios/payload-studio",
3
- "version": "0.6.0-beta.23",
3
+ "version": "0.6.0-beta.25",
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",