@orion-studios/payload-studio 0.5.0-beta.84 → 0.5.0-beta.86

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.
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-ADIIWIYL.mjs";
4
4
  import {
5
5
  studioDocumentToLayout
6
- } from "./chunk-ZMADCW3Y.mjs";
6
+ } from "./chunk-FWVVRZ32.mjs";
7
7
  import {
8
8
  __export
9
9
  } from "./chunk-6BWS3CLP.mjs";
@@ -743,7 +743,7 @@ var inspectorDefinitionByBlockType = {
743
743
  };
744
744
 
745
745
  // src/studio-pages/builder/settings-v2/BlockInspectorRenderer.tsx
746
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
746
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
747
747
 
748
748
  // src/studio-pages/migrations.ts
749
749
  var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
package/dist/index.mjs CHANGED
@@ -9,13 +9,13 @@ import {
9
9
  } from "./chunk-XK3K5GRP.mjs";
10
10
  import {
11
11
  nextjs_exports
12
- } from "./chunk-PGOTV7IF.mjs";
12
+ } from "./chunk-AFLEATYB.mjs";
13
13
  import {
14
14
  studio_exports
15
15
  } from "./chunk-ADIIWIYL.mjs";
16
16
  import {
17
17
  studio_pages_exports
18
- } from "./chunk-ZMADCW3Y.mjs";
18
+ } from "./chunk-FWVVRZ32.mjs";
19
19
  import "./chunk-SIL2J5MF.mjs";
20
20
  import "./chunk-6BWS3CLP.mjs";
21
21
  export {
@@ -4,9 +4,9 @@ import {
4
4
  createPayloadClient,
5
5
  createSiteQueries,
6
6
  resolveMedia
7
- } from "../chunk-PGOTV7IF.mjs";
7
+ } from "../chunk-AFLEATYB.mjs";
8
8
  import "../chunk-ADIIWIYL.mjs";
9
- import "../chunk-ZMADCW3Y.mjs";
9
+ import "../chunk-FWVVRZ32.mjs";
10
10
  import "../chunk-SIL2J5MF.mjs";
11
11
  import "../chunk-6BWS3CLP.mjs";
12
12
  export {
@@ -447,7 +447,7 @@ function ImageControls({
447
447
  )
448
448
  ] }),
449
449
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "orion-builder-settings-label", children: [
450
- "Focus X (%)",
450
+ "Horizontal Image Focus (%)",
451
451
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "orion-builder-settings-input-split", children: [
452
452
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
453
453
  "input",
@@ -473,7 +473,7 @@ function ImageControls({
473
473
  ] })
474
474
  ] }),
475
475
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "orion-builder-settings-label", children: [
476
- "Focus Y (%)",
476
+ "Vertical Image Focus (%)",
477
477
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "orion-builder-settings-input-split", children: [
478
478
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
479
479
  "input",
@@ -1023,9 +1023,11 @@ var mediaLabel = (item) => item.filename || item.alt || `Media #${item.id}`;
1023
1023
  var groupLabel = (key) => commonInspectorGroups.find((group) => group.key === key)?.label || key;
1024
1024
  function BlockInspectorRenderer({
1025
1025
  block,
1026
- blockMedia,
1027
1026
  blockType,
1027
+ mediaImageControls,
1028
+ mediaSources,
1028
1029
  mode,
1030
+ onMediaImageChange,
1029
1031
  onModeChange,
1030
1032
  onSearchQueryChange,
1031
1033
  onUpdateField,
@@ -1050,12 +1052,24 @@ function BlockInspectorRenderer({
1050
1052
  });
1051
1053
  }, [block, fields, mode, searchQuery]);
1052
1054
  const groups = (0, import_react.useMemo)(() => {
1053
- return commonInspectorGroups.map((group) => ({
1055
+ const baseGroups = commonInspectorGroups.map((group) => ({
1054
1056
  key: group.key,
1055
1057
  label: group.label,
1056
1058
  fields: resolvedFields.filter((field) => field.group === group.key)
1057
1059
  })).filter((group) => group.fields.length > 0);
1058
- }, [resolvedFields]);
1060
+ const hasExplicitMediaControls = Boolean(mediaImageControls) || Array.isArray(mediaSources) && mediaSources.length > 0;
1061
+ if (!hasExplicitMediaControls || baseGroups.some((group) => group.key === "media")) {
1062
+ return baseGroups;
1063
+ }
1064
+ return [
1065
+ ...baseGroups,
1066
+ {
1067
+ key: "media",
1068
+ label: groupLabel("media"),
1069
+ fields: []
1070
+ }
1071
+ ];
1072
+ }, [mediaImageControls, mediaSources, resolvedFields]);
1059
1073
  if (!definition) {
1060
1074
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-empty", children: "No V2 schema has been registered for this section yet." });
1061
1075
  }
@@ -1113,9 +1127,18 @@ function BlockInspectorRenderer({
1113
1127
  const mediaPositionX = normalizeNumber(getByPath(block, "settings.media.positionX"), 50);
1114
1128
  const mediaPositionY = normalizeNumber(getByPath(block, "settings.media.positionY"), 50);
1115
1129
  const mediaHeight = getByPath(block, "settings.media.height");
1116
- const selectedBlockMedia = blockMedia ? toMediaLibraryItem(blockMedia.value) : null;
1117
- const selectedBlockMediaID = blockMedia ? getRelationID(blockMedia.value) : null;
1118
- const blockMediaOptions = blockMedia && selectedBlockMedia && !blockMedia.library.some((item) => String(item.id) === String(selectedBlockMedia.id)) ? [selectedBlockMedia, ...blockMedia.library] : blockMedia?.library || [];
1130
+ const fallbackMediaPosition = mediaPosition === "top" || mediaPosition === "bottom" || mediaPosition === "left" || mediaPosition === "right" ? mediaPosition : "center";
1131
+ const fallbackMediaControls = {
1132
+ cornerStyle: mediaCornerStyle === "square" ? "square" : "rounded",
1133
+ fit: mediaFit === "contain" ? "contain" : "cover",
1134
+ height: typeof mediaHeight === "number" ? mediaHeight : null,
1135
+ position: fallbackMediaPosition,
1136
+ positionX: mediaPositionX,
1137
+ positionY: mediaPositionY
1138
+ };
1139
+ const effectiveMedia = mediaImageControls ?? fallbackMediaControls;
1140
+ const effectiveMediaSources = Array.isArray(mediaSources) ? mediaSources : [];
1141
+ const hasMediaGroupContent = group.key === "media" && (effectiveMediaSources.length > 0 || Boolean(effectiveMedia));
1119
1142
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1120
1143
  Accordion,
1121
1144
  {
@@ -1127,70 +1150,90 @@ function BlockInspectorRenderer({
1127
1150
  subtitle: `${group.fields.length} setting${group.fields.length === 1 ? "" : "s"}`,
1128
1151
  title: group.label,
1129
1152
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-builder-settings-field-list", children: [
1130
- group.key === "media" && blockMedia ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1131
- blockMedia.loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-note", children: "Loading media library..." }) : null,
1132
- blockMedia.error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-error", children: blockMedia.error }) : null,
1133
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1134
- blockMedia.label,
1135
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1136
- "select",
1137
- {
1138
- className: "orion-builder-settings-input",
1139
- onChange: (event) => blockMedia.onSelect(event.target.value),
1140
- value: selectedBlockMediaID !== null ? String(selectedBlockMediaID) : "",
1141
- children: [
1142
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "No image" }),
1143
- blockMediaOptions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: String(item.id), children: mediaLabel(item) }, String(item.id)))
1144
- ]
1145
- }
1146
- )
1147
- ] }),
1148
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1149
- "button",
1153
+ group.key === "media" ? effectiveMediaSources.map((source) => {
1154
+ const selectedSourceMedia = toMediaLibraryItem(source.value);
1155
+ const selectedSourceMediaID = getRelationID(source.value);
1156
+ const sourceOptions = selectedSourceMedia && !source.library.some((item) => String(item.id) === String(selectedSourceMedia.id)) ? [selectedSourceMedia, ...source.library] : source.library;
1157
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1158
+ "div",
1150
1159
  {
1151
- className: "orion-builder-settings-inline-btn",
1152
- disabled: selectedBlockMediaID === null,
1153
- onClick: blockMedia.onRemove,
1154
- type: "button",
1155
- children: "Remove Image"
1156
- }
1157
- ),
1158
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1159
- blockMedia.uploadLabel,
1160
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1161
- "input",
1162
- {
1163
- accept: "image/*",
1164
- className: "orion-builder-settings-input",
1165
- disabled: blockMedia.uploadDisabled,
1166
- onChange: (event) => {
1167
- const file = event.currentTarget.files?.[0];
1168
- if (file) {
1169
- blockMedia.onUpload(file);
1160
+ className: "orion-builder-settings-item-card",
1161
+ style: { padding: "0.56rem" },
1162
+ children: [
1163
+ source.loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-note", children: "Loading media library..." }) : null,
1164
+ source.error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-error", children: source.error }) : null,
1165
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1166
+ source.label,
1167
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1168
+ "select",
1169
+ {
1170
+ className: "orion-builder-settings-input",
1171
+ onChange: (event) => source.onSelect(event.target.value),
1172
+ value: selectedSourceMediaID !== null ? String(selectedSourceMediaID) : "",
1173
+ children: [
1174
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "No image" }),
1175
+ sourceOptions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: String(item.id), children: mediaLabel(item) }, String(item.id)))
1176
+ ]
1177
+ }
1178
+ )
1179
+ ] }),
1180
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1181
+ "button",
1182
+ {
1183
+ className: "orion-builder-settings-inline-btn",
1184
+ disabled: selectedSourceMediaID === null,
1185
+ onClick: source.onRemove,
1186
+ type: "button",
1187
+ children: "Remove Image"
1170
1188
  }
1171
- event.currentTarget.value = "";
1172
- },
1173
- type: "file"
1174
- }
1175
- )
1176
- ] }),
1177
- blockMedia.uploading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-note", children: "Uploading image..." }) : null
1178
- ] }) : null,
1179
- group.key === "media" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1189
+ ),
1190
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1191
+ source.uploadLabel,
1192
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1193
+ "input",
1194
+ {
1195
+ accept: "image/*",
1196
+ className: "orion-builder-settings-input",
1197
+ disabled: source.uploadDisabled,
1198
+ onChange: (event) => {
1199
+ const file = event.currentTarget.files?.[0];
1200
+ if (file) {
1201
+ source.onUpload(file);
1202
+ }
1203
+ event.currentTarget.value = "";
1204
+ },
1205
+ type: "file"
1206
+ }
1207
+ )
1208
+ ] }),
1209
+ source.uploading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-note", children: "Uploading image..." }) : null
1210
+ ]
1211
+ },
1212
+ `media-source-${source.label}`
1213
+ );
1214
+ }) : null,
1215
+ group.key === "media" && effectiveMedia ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1180
1216
  ImageControls,
1181
1217
  {
1182
- cornerStyle: mediaCornerStyle === "square" ? "square" : "rounded",
1183
- fit: mediaFit === "contain" ? "contain" : "cover",
1184
- height: typeof mediaHeight === "number" ? mediaHeight : null,
1218
+ cornerStyle: effectiveMedia.cornerStyle,
1219
+ fit: effectiveMedia.fit,
1220
+ height: effectiveMedia.height,
1221
+ heightLabel: effectiveMedia.heightLabel,
1222
+ maxHeight: effectiveMedia.maxHeight,
1223
+ minHeight: effectiveMedia.minHeight,
1185
1224
  onChange: (field, value) => {
1225
+ if (onMediaImageChange) {
1226
+ onMediaImageChange(field, value);
1227
+ return;
1228
+ }
1186
1229
  updateForKey(`settings.media.${field}`, value);
1187
1230
  },
1188
- position: mediaPosition === "top" || mediaPosition === "bottom" || mediaPosition === "left" || mediaPosition === "right" ? mediaPosition : "center",
1189
- positionX: mediaPositionX,
1190
- positionY: mediaPositionY
1231
+ position: effectiveMedia.position,
1232
+ positionX: effectiveMedia.positionX,
1233
+ positionY: effectiveMedia.positionY
1191
1234
  }
1192
1235
  ) : null,
1193
- group.fields.filter((field) => !(group.key === "media" && field.key.startsWith("settings.media."))).map((field) => {
1236
+ (hasMediaGroupContent ? group.fields.filter((field) => !(group.key === "media" && field.key.startsWith("settings.media."))) : group.fields).map((field) => {
1194
1237
  const fieldValue = getByPath(block, field.key);
1195
1238
  if (field.type === "checkbox") {
1196
1239
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label is-checkbox", children: [
@@ -2204,6 +2247,18 @@ function BlockFrame({
2204
2247
 
2205
2248
  // src/studio-pages/builder/renderers/renderSimpleBlockPreview.tsx
2206
2249
  var import_jsx_runtime8 = require("react/jsx-runtime");
2250
+ var parseOptionalPercentNumber = (value) => {
2251
+ if (typeof value === "number" && Number.isFinite(value)) {
2252
+ return Math.max(0, Math.min(100, value));
2253
+ }
2254
+ if (typeof value === "string" && value.trim().length > 0) {
2255
+ const parsed = Number(value);
2256
+ if (Number.isFinite(parsed)) {
2257
+ return Math.max(0, Math.min(100, parsed));
2258
+ }
2259
+ }
2260
+ return void 0;
2261
+ };
2207
2262
  function renderSimpleBlockPreview(args) {
2208
2263
  const {
2209
2264
  block,
@@ -2387,10 +2442,14 @@ function renderSimpleBlockPreview(args) {
2387
2442
  if (type === "media") {
2388
2443
  const image = resolveMedia2(block.image);
2389
2444
  const size = normalizeText3(block.size, "default");
2445
+ const imagePositionX = parseOptionalPercentNumber(block?.imagePositionX);
2446
+ const imagePositionY = parseOptionalPercentNumber(block?.imagePositionY);
2390
2447
  const imageStyle = getImagePresentationStyle2({
2391
2448
  cornerStyle: normalizeImageCornerStyle3(block?.imageCornerStyle),
2392
2449
  fit: normalizeImageFit3(block?.imageFit),
2393
- position: normalizeImagePosition3(block?.imagePosition)
2450
+ position: normalizeImagePosition3(block?.imagePosition),
2451
+ positionX: imagePositionX,
2452
+ positionY: imagePositionY
2394
2453
  });
2395
2454
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2396
2455
  BlockFrame,
@@ -3345,25 +3404,25 @@ function ArrayItemsEditor({
3345
3404
  minHeight: blockType === "logoWall" ? 24 : blockType === "beforeAfter" ? 60 : 40,
3346
3405
  onChange: (field, value) => {
3347
3406
  if (field === "height") {
3348
- onUpdateItemField(itemIndex, "imageHeight", value);
3407
+ onUpdateItemSetting(itemIndex, "media.height", value);
3349
3408
  return;
3350
3409
  }
3351
3410
  if (field === "fit") {
3352
- onUpdateItemField(itemIndex, "imageFit", value);
3411
+ onUpdateItemSetting(itemIndex, "media.fit", value);
3353
3412
  return;
3354
3413
  }
3355
3414
  if (field === "cornerStyle") {
3356
- onUpdateItemField(itemIndex, "imageCornerStyle", value);
3415
+ onUpdateItemSetting(itemIndex, "media.cornerStyle", value);
3357
3416
  return;
3358
3417
  }
3359
3418
  if (field === "position") {
3360
- onUpdateItemField(itemIndex, "imagePosition", value);
3361
- onUpdateItemField(itemIndex, "imagePositionX", null);
3362
- onUpdateItemField(itemIndex, "imagePositionY", null);
3419
+ onUpdateItemSetting(itemIndex, "media.position", value);
3420
+ onUpdateItemSetting(itemIndex, "media.positionX", null);
3421
+ onUpdateItemSetting(itemIndex, "media.positionY", null);
3363
3422
  return;
3364
3423
  }
3365
3424
  if (field === "positionX" || field === "positionY") {
3366
- onUpdateItemField(itemIndex, field === "positionX" ? "imagePositionX" : "imagePositionY", value);
3425
+ onUpdateItemSetting(itemIndex, `media.${field}`, value);
3367
3426
  }
3368
3427
  },
3369
3428
  position: normalizeImagePosition(item.imagePosition),
@@ -3777,7 +3836,7 @@ function parseTestimonialRating(value, fallback = 5) {
3777
3836
  function parsePercentNumber(value, fallback) {
3778
3837
  return Math.max(0, Math.min(100, parsePixelNumber(value, fallback)));
3779
3838
  }
3780
- function parseOptionalPercentNumber(value) {
3839
+ function parseOptionalPercentNumber2(value) {
3781
3840
  if (typeof value === "number" && Number.isFinite(value)) {
3782
3841
  return Math.max(0, Math.min(100, value));
3783
3842
  }
@@ -4068,6 +4127,7 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
4068
4127
  const isArrayItemBlockSelected = selectedType === "featureGrid" || selectedType === "logoWall" || selectedType === "beforeAfter" || selectedType === "stats" || selectedType === "faq" || selectedType === "testimonials";
4069
4128
  const selectedBlockHasMediaSource = selectedType === "hero" || selectedType === "media";
4070
4129
  const selectedBlockMediaValue = selectedType === "hero" ? selectedBlock?.media : selectedType === "media" ? selectedBlock?.image : null;
4130
+ const selectedItemRecord = typeof selectedItemIndex === "number" && selectedItemIndex >= 0 && selectedItemIndex < selectedItems.length && isRecord5(selectedItems[selectedItemIndex]) ? selectedItems[selectedItemIndex] : null;
4071
4131
  const editCopyInPanelEnabled = Boolean(selectedBlockAdvancedSettings.editCopyInPanel);
4072
4132
  const isBlockUploadTarget = (blockIndex, kind) => selectedIndex === blockIndex && uploadingTarget?.kind === kind;
4073
4133
  const isFeatureGridItemUploading = (blockIndex, itemIndex) => selectedIndex === blockIndex && uploadingTarget?.kind === "featureGridItem" && uploadingTarget.itemIndex === itemIndex;
@@ -4393,6 +4453,36 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
4393
4453
  void uploadMediaForSelected({ kind: "media" }, file);
4394
4454
  }
4395
4455
  };
4456
+ const updateSelectedItemMediaPresentationFromInspector = (field, value) => {
4457
+ if (selectedIndex === null || typeof selectedItemIndex !== "number") {
4458
+ return;
4459
+ }
4460
+ if (field === "height") {
4461
+ updateArrayItemSettingField(selectedIndex, "items", selectedItemIndex, "media.height", value);
4462
+ return;
4463
+ }
4464
+ if (field === "fit") {
4465
+ updateArrayItemSettingField(selectedIndex, "items", selectedItemIndex, "media.fit", value);
4466
+ return;
4467
+ }
4468
+ if (field === "cornerStyle") {
4469
+ updateArrayItemSettingField(selectedIndex, "items", selectedItemIndex, "media.cornerStyle", value);
4470
+ return;
4471
+ }
4472
+ if (field === "position") {
4473
+ updateArrayItemSettingField(selectedIndex, "items", selectedItemIndex, "media.position", value);
4474
+ updateArrayItemSettingField(selectedIndex, "items", selectedItemIndex, "media.positionX", null);
4475
+ updateArrayItemSettingField(selectedIndex, "items", selectedItemIndex, "media.positionY", null);
4476
+ return;
4477
+ }
4478
+ if (field === "positionX") {
4479
+ updateArrayItemSettingField(selectedIndex, "items", selectedItemIndex, "media.positionX", value);
4480
+ return;
4481
+ }
4482
+ if (field === "positionY") {
4483
+ updateArrayItemSettingField(selectedIndex, "items", selectedItemIndex, "media.positionY", value);
4484
+ }
4485
+ };
4396
4486
  const uploadItemMediaFromV2 = (itemIndex, field, file) => {
4397
4487
  if (selectedType === "featureGrid" && field === "media") {
4398
4488
  void uploadMediaForSelected({ field: "media", itemIndex, kind: "featureGridItem" }, file);
@@ -4875,6 +4965,97 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
4875
4965
  setSidebarOpen(true);
4876
4966
  setActiveSidebarPanel("addSections");
4877
4967
  }, [layout.length]);
4968
+ const selectedMediaSources = selectedBlockHasMediaSource ? [
4969
+ {
4970
+ error: mediaLibraryError,
4971
+ label: selectedType === "hero" ? "Hero Image" : "Section Image",
4972
+ library: mediaLibrary,
4973
+ loading: mediaLibraryLoading,
4974
+ onRemove: () => setSelectedBlockMediaFromLibrary(""),
4975
+ onSelect: setSelectedBlockMediaFromLibrary,
4976
+ onUpload: uploadSelectedBlockMediaFromV2,
4977
+ uploadDisabled: uploadingTarget !== null,
4978
+ uploadLabel: selectedType === "hero" ? "Upload Hero Image" : "Upload Image",
4979
+ uploading: isSelectedBlockMediaUploading,
4980
+ value: selectedBlockMediaValue
4981
+ }
4982
+ ] : selectedItemRecord && typeof selectedItemIndex === "number" && selectedType === "featureGrid" ? [
4983
+ {
4984
+ error: mediaLibraryError,
4985
+ label: `Feature Image (Item ${selectedItemIndex + 1})`,
4986
+ library: mediaLibrary,
4987
+ loading: mediaLibraryLoading,
4988
+ onRemove: () => setSelectedItemMediaFieldFromLibrary(selectedItemIndex, "media", ""),
4989
+ onSelect: (mediaID) => setSelectedItemMediaFieldFromLibrary(selectedItemIndex, "media", mediaID),
4990
+ onUpload: (file) => uploadItemMediaFromV2(selectedItemIndex, "media", file),
4991
+ uploadDisabled: uploadingTarget !== null,
4992
+ uploadLabel: "Upload Feature Image",
4993
+ uploading: isSelectedItemMediaUploading(selectedItemIndex, "media"),
4994
+ value: selectedItemRecord.media
4995
+ }
4996
+ ] : selectedItemRecord && typeof selectedItemIndex === "number" && selectedType === "logoWall" ? [
4997
+ {
4998
+ error: mediaLibraryError,
4999
+ label: `Logo Image (Item ${selectedItemIndex + 1})`,
5000
+ library: mediaLibrary,
5001
+ loading: mediaLibraryLoading,
5002
+ onRemove: () => setSelectedItemMediaFieldFromLibrary(selectedItemIndex, "media", ""),
5003
+ onSelect: (mediaID) => setSelectedItemMediaFieldFromLibrary(selectedItemIndex, "media", mediaID),
5004
+ onUpload: (file) => uploadItemMediaFromV2(selectedItemIndex, "media", file),
5005
+ uploadDisabled: uploadingTarget !== null,
5006
+ uploadLabel: "Upload Logo Image",
5007
+ uploading: isSelectedItemMediaUploading(selectedItemIndex, "media"),
5008
+ value: selectedItemRecord.media
5009
+ }
5010
+ ] : selectedItemRecord && typeof selectedItemIndex === "number" && selectedType === "beforeAfter" ? [
5011
+ {
5012
+ error: mediaLibraryError,
5013
+ label: `Before Image (Item ${selectedItemIndex + 1})`,
5014
+ library: mediaLibrary,
5015
+ loading: mediaLibraryLoading,
5016
+ onRemove: () => setSelectedItemMediaFieldFromLibrary(selectedItemIndex, "beforeMedia", ""),
5017
+ onSelect: (mediaID) => setSelectedItemMediaFieldFromLibrary(selectedItemIndex, "beforeMedia", mediaID),
5018
+ onUpload: (file) => uploadItemMediaFromV2(selectedItemIndex, "beforeMedia", file),
5019
+ uploadDisabled: uploadingTarget !== null,
5020
+ uploadLabel: "Upload Before Image",
5021
+ uploading: isSelectedItemMediaUploading(selectedItemIndex, "beforeMedia"),
5022
+ value: selectedItemRecord.beforeMedia
5023
+ },
5024
+ {
5025
+ error: mediaLibraryError,
5026
+ label: `After Image (Item ${selectedItemIndex + 1})`,
5027
+ library: mediaLibrary,
5028
+ loading: mediaLibraryLoading,
5029
+ onRemove: () => setSelectedItemMediaFieldFromLibrary(selectedItemIndex, "afterMedia", ""),
5030
+ onSelect: (mediaID) => setSelectedItemMediaFieldFromLibrary(selectedItemIndex, "afterMedia", mediaID),
5031
+ onUpload: (file) => uploadItemMediaFromV2(selectedItemIndex, "afterMedia", file),
5032
+ uploadDisabled: uploadingTarget !== null,
5033
+ uploadLabel: "Upload After Image",
5034
+ uploading: isSelectedItemMediaUploading(selectedItemIndex, "afterMedia"),
5035
+ value: selectedItemRecord.afterMedia
5036
+ }
5037
+ ] : [];
5038
+ const selectedMediaImageControls = selectedItemRecord && typeof selectedItemIndex === "number" && (selectedType === "featureGrid" || selectedType === "logoWall" || selectedType === "beforeAfter") ? {
5039
+ cornerStyle: normalizeImageCornerStyle2(selectedItemRecord.imageCornerStyle),
5040
+ fit: normalizeImageFit2(selectedItemRecord.imageFit),
5041
+ height: (() => {
5042
+ if (typeof selectedItemRecord.imageHeight === "number" && Number.isFinite(selectedItemRecord.imageHeight)) {
5043
+ return selectedItemRecord.imageHeight;
5044
+ }
5045
+ if (typeof selectedItemRecord.imageHeight === "string" && selectedItemRecord.imageHeight.trim().length > 0) {
5046
+ const parsed = Number(selectedItemRecord.imageHeight);
5047
+ if (Number.isFinite(parsed)) {
5048
+ return parsed;
5049
+ }
5050
+ }
5051
+ return null;
5052
+ })(),
5053
+ maxHeight: selectedType === "logoWall" ? 200 : 600,
5054
+ minHeight: selectedType === "logoWall" ? 24 : selectedType === "beforeAfter" ? 60 : 40,
5055
+ position: normalizeImagePosition2(selectedItemRecord.imagePosition),
5056
+ positionX: parsePercentNumber(selectedItemRecord.imagePositionX, 50),
5057
+ positionY: parsePercentNumber(selectedItemRecord.imagePositionY, 50)
5058
+ } : void 0;
4878
5059
  (0, import_react5.useEffect)(() => {
4879
5060
  return;
4880
5061
  }, [layout]);
@@ -5107,6 +5288,8 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
5107
5288
  block.backgroundImageFit
5108
5289
  );
5109
5290
  const backgroundImagePosition = normalizeHeroImagePosition(block.backgroundImagePosition);
5291
+ const backgroundImagePositionX = parseOptionalPercentNumber2(block.backgroundImagePositionX);
5292
+ const backgroundImagePositionY = parseOptionalPercentNumber2(block.backgroundImagePositionY);
5110
5293
  const heroHeight = normalizeHeroHeight(block.heroHeight);
5111
5294
  const heroMinHeight = heroHeight === "full" ? "100svh" : heroHeight === "md" ? resolveBuilderMediumHeroHeight(topViewportHeight) : "360px";
5112
5295
  const heroCornerRadius = getHeroImageCornerRadius(backgroundImageCornerStyle);
@@ -5116,6 +5299,10 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
5116
5299
  );
5117
5300
  const backgroundImageURL = normalizeText2(block.backgroundImageURL);
5118
5301
  const backgroundImage = media?.url || backgroundImageURL;
5302
+ const backgroundPositionBase = positionPercent(backgroundImagePosition, backgroundImageFit);
5303
+ const resolvedBackgroundPositionX = typeof backgroundImagePositionX === "number" ? backgroundImagePositionX : backgroundPositionBase.x;
5304
+ const resolvedBackgroundPositionY = typeof backgroundImagePositionY === "number" ? backgroundImagePositionY : backgroundPositionBase.y;
5305
+ const backgroundImageObjectPosition = `${resolvedBackgroundPositionX}% ${resolvedBackgroundPositionY}%`;
5119
5306
  const hasCustomHeroColor = backgroundColor.length > 0 && backgroundColor.toLowerCase() !== "#124a37";
5120
5307
  const overlayModeRaw = normalizeText2(block?.backgroundOverlayMode, "none");
5121
5308
  const overlayMode = overlayModeRaw === "solid" || overlayModeRaw === "gradient" ? overlayModeRaw : "none";
@@ -5149,7 +5336,7 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
5149
5336
  const mediaStyle = backgroundImage && variant === "default" ? {
5150
5337
  ...hasCustomHeroColor ? { backgroundColor } : {},
5151
5338
  backgroundImage: overlayLayer ? `${overlayLayer}, url('${backgroundImage}')` : `url('${backgroundImage}')`,
5152
- backgroundPosition: overlayLayer ? `center center, ${backgroundImagePosition}` : backgroundImagePosition,
5339
+ backgroundPosition: overlayLayer ? `center center, ${backgroundImageObjectPosition}` : backgroundImageObjectPosition,
5153
5340
  backgroundRepeat: overlayLayer ? "no-repeat, no-repeat" : "no-repeat",
5154
5341
  backgroundSize: overlayLayer ? `cover, ${backgroundImageFit}` : backgroundImageFit,
5155
5342
  minHeight: heroMinHeight
@@ -5282,8 +5469,8 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
5282
5469
  const iconType = normalizeText2(itemRecord?.iconType, "badge");
5283
5470
  const iconBadge = normalizeText2(itemRecord?.icon);
5284
5471
  const iconLucide = normalizeText2(itemRecord?.iconLucide);
5285
- const itemPositionX = parseOptionalPercentNumber(itemRecord?.imagePositionX);
5286
- const itemPositionY = parseOptionalPercentNumber(itemRecord?.imagePositionY);
5472
+ const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5473
+ const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5287
5474
  const itemImageStyle = getImagePresentationStyle({
5288
5475
  cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5289
5476
  fit: normalizeImageFit2(itemRecord?.imageFit),
@@ -5397,8 +5584,8 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
5397
5584
  const iconType = normalizeText2(itemRecord?.iconType, "badge");
5398
5585
  const iconBadge = normalizeText2(itemRecord?.icon);
5399
5586
  const iconLucide = normalizeText2(itemRecord?.iconLucide);
5400
- const itemPositionX = parseOptionalPercentNumber(itemRecord?.imagePositionX);
5401
- const itemPositionY = parseOptionalPercentNumber(itemRecord?.imagePositionY);
5587
+ const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5588
+ const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5402
5589
  const itemImageStyle = getImagePresentationStyle({
5403
5590
  cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5404
5591
  fit: normalizeImageFit2(itemRecord?.imageFit),
@@ -5601,8 +5788,8 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
5601
5788
  const itemRecord = item;
5602
5789
  const media = resolveMedia(itemRecord?.media);
5603
5790
  const imageHeight = parsePixelNumber(itemRecord?.imageHeight, 54);
5604
- const itemPositionX = parseOptionalPercentNumber(itemRecord?.imagePositionX);
5605
- const itemPositionY = parseOptionalPercentNumber(itemRecord?.imagePositionY);
5791
+ const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5792
+ const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5606
5793
  const imageStyle = getImagePresentationStyle({
5607
5794
  cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5608
5795
  fit: normalizeImageFit2(itemRecord?.imageFit),
@@ -5708,8 +5895,8 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
5708
5895
  const beforeMedia = resolveMedia(itemRecord?.beforeMedia);
5709
5896
  const afterMedia = resolveMedia(itemRecord?.afterMedia);
5710
5897
  const imageHeight = parsePixelNumber(itemRecord?.imageHeight, 160);
5711
- const itemPositionX = parseOptionalPercentNumber(itemRecord?.imagePositionX);
5712
- const itemPositionY = parseOptionalPercentNumber(itemRecord?.imagePositionY);
5898
+ const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5899
+ const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5713
5900
  const imageStyle = getImagePresentationStyle({
5714
5901
  cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
5715
5902
  fit: normalizeImageFit2(itemRecord?.imageFit),
@@ -6232,21 +6419,11 @@ function BuilderPageEditor({ featureFlags: _featureFlags, initialDoc, pageID, si
6232
6419
  BlockInspectorRenderer,
6233
6420
  {
6234
6421
  block: selectedBlock,
6235
- blockMedia: selectedBlockHasMediaSource ? {
6236
- error: mediaLibraryError,
6237
- label: selectedType === "hero" ? "Hero Image" : "Section Image",
6238
- library: mediaLibrary,
6239
- loading: mediaLibraryLoading,
6240
- onRemove: () => setSelectedBlockMediaFromLibrary(""),
6241
- onSelect: setSelectedBlockMediaFromLibrary,
6242
- onUpload: uploadSelectedBlockMediaFromV2,
6243
- uploadDisabled: uploadingTarget !== null,
6244
- uploadLabel: selectedType === "hero" ? "Upload Hero Image" : "Upload Image",
6245
- uploading: isSelectedBlockMediaUploading,
6246
- value: selectedBlockMediaValue
6247
- } : void 0,
6248
6422
  blockType: selectedType,
6423
+ mediaImageControls: selectedMediaImageControls,
6424
+ mediaSources: selectedMediaSources,
6249
6425
  mode: settingsPanelMode,
6426
+ onMediaImageChange: selectedMediaImageControls ? updateSelectedItemMediaPresentationFromInspector : void 0,
6250
6427
  onModeChange: setSettingsPanelMode,
6251
6428
  onSearchQueryChange: setSettingsSearchQuery,
6252
6429
  onUpdateField: updateSelectedField,