@orion-studios/payload-studio 0.6.0-beta.13 → 0.6.0-beta.130

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/admin/client.js +2061 -538
  2. package/dist/admin/client.mjs +2078 -568
  3. package/dist/admin/index.js +124 -15
  4. package/dist/admin/index.mjs +1 -1
  5. package/dist/admin-app/client.js +14 -5
  6. package/dist/admin-app/client.mjs +1 -1
  7. package/dist/admin-app/styles.css +277 -1
  8. package/dist/admin.css +98 -2
  9. package/dist/builder-v2/client.d.mts +18 -0
  10. package/dist/builder-v2/client.d.ts +18 -0
  11. package/dist/builder-v2/client.js +3832 -0
  12. package/dist/builder-v2/client.mjs +3707 -0
  13. package/dist/builder-v2/index.d.mts +248 -0
  14. package/dist/builder-v2/index.d.ts +248 -0
  15. package/dist/builder-v2/index.js +805 -0
  16. package/dist/builder-v2/index.mjs +755 -0
  17. package/dist/builder-v2/styles.css +2514 -0
  18. package/dist/{chunk-PF3EBZXF.mjs → chunk-7ZMXZRBP.mjs} +39 -3
  19. package/dist/{chunk-XZQILJK3.mjs → chunk-JC3UV74N.mjs} +124 -15
  20. package/dist/{chunk-KPIX7OSV.mjs → chunk-NF37A575.mjs} +14 -5
  21. package/dist/{chunk-XKUTZ7IU.mjs → chunk-NGLIA2OE.mjs} +53 -2
  22. package/dist/{chunk-OTHERBGX.mjs → chunk-ZADL33R6.mjs} +1 -1
  23. package/dist/{index-Cv-6qnrw.d.mts → index-D5zrOdyv.d.mts} +3 -1
  24. package/dist/{index-Crx_MtPw.d.ts → index-Dv-Alx4h.d.ts} +3 -1
  25. package/dist/index.d.mts +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +215 -19
  28. package/dist/index.mjs +4 -4
  29. package/dist/nextjs/index.js +39 -3
  30. package/dist/nextjs/index.mjs +2 -2
  31. package/dist/studio-pages/builder.css +66 -5
  32. package/dist/studio-pages/client.js +618 -73
  33. package/dist/studio-pages/client.mjs +641 -96
  34. package/dist/studio-pages/index.d.mts +1 -1
  35. package/dist/studio-pages/index.d.ts +1 -1
  36. package/dist/studio-pages/index.js +91 -4
  37. package/dist/studio-pages/index.mjs +2 -2
  38. package/package.json +22 -3
@@ -97,7 +97,9 @@ var defaultBuilderBlockSettingsV2 = {
97
97
  },
98
98
  typography: {
99
99
  bodyAlign: "left",
100
+ bodySizePt: null,
100
101
  headingAlign: "left",
102
+ headingSizePt: null,
101
103
  letterSpacingPreset: "normal",
102
104
  lineHeightPreset: "normal",
103
105
  maxTextWidth: "auto"
@@ -126,7 +128,9 @@ var defaultBuilderItemSettingsV2 = {
126
128
  },
127
129
  typography: {
128
130
  bodyAlign: "left",
131
+ bodySizePt: null,
129
132
  headingAlign: "left",
133
+ headingSizePt: null,
130
134
  letterSpacingPreset: "normal",
131
135
  lineHeightPreset: "normal",
132
136
  maxTextWidth: "auto"
@@ -156,6 +160,7 @@ var defaultBuilderThemeTokens = {
156
160
 
157
161
  // src/studio-pages/builder/adapters/settingsV2.ts
158
162
  var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
163
+ var isTextAlign = (value) => value === "left" || value === "center" || value === "right" || value === "justify";
159
164
  var parsePercent = (value) => {
160
165
  if (typeof value === "number" && Number.isFinite(value)) {
161
166
  return Math.max(0, Math.min(100, value));
@@ -180,6 +185,18 @@ var parsePixel = (value) => {
180
185
  }
181
186
  return null;
182
187
  };
188
+ var parsePoint = (value) => {
189
+ if (typeof value === "number" && Number.isFinite(value)) {
190
+ return Math.max(6, Math.min(120, Math.round(value)));
191
+ }
192
+ if (typeof value === "string" && value.trim().length > 0) {
193
+ const parsed = Number(value);
194
+ if (Number.isFinite(parsed)) {
195
+ return Math.max(6, Math.min(120, Math.round(parsed)));
196
+ }
197
+ }
198
+ return null;
199
+ };
183
200
  var mergeSettings = (defaults, input) => {
184
201
  if (!isRecord(input)) {
185
202
  return structuredClone(defaults);
@@ -198,6 +215,10 @@ var mergeSettings = (defaults, input) => {
198
215
  };
199
216
  var legacyBlockToV2Settings = (block) => {
200
217
  const current = structuredClone(defaultBuilderBlockSettingsV2);
218
+ if (block.blockType === "hero" && block.variant === "centered") {
219
+ current.typography.headingAlign = "center";
220
+ current.typography.bodyAlign = "center";
221
+ }
201
222
  current.layout.contentWidth = block.contentWidth === "narrow" || block.contentWidth === "content" || block.contentWidth === "wide" || block.contentWidth === "full" || block.contentWidth === "inherit" ? block.contentWidth : current.layout.contentWidth;
202
223
  current.layout.sectionPaddingX = block.sectionPaddingX === "none" || block.sectionPaddingX === "sm" || block.sectionPaddingX === "md" || block.sectionPaddingX === "lg" || block.sectionPaddingX === "inherit" ? block.sectionPaddingX : current.layout.sectionPaddingX;
203
224
  current.layout.sectionPaddingY = block.sectionPaddingY === "none" || block.sectionPaddingY === "sm" || block.sectionPaddingY === "lg" ? block.sectionPaddingY : current.layout.sectionPaddingY;
@@ -234,15 +255,28 @@ var legacyBlockToV2Settings = (block) => {
234
255
  current.media.positionX = parsePercent(block.imagePositionX ?? block.backgroundImagePositionX);
235
256
  current.media.positionY = parsePercent(block.imagePositionY ?? block.backgroundImagePositionY);
236
257
  current.media.height = parsePixel(block.imageHeight);
237
- current.typography.headingAlign = block.textHeadingAlign === "left" || block.textHeadingAlign === "center" || block.textHeadingAlign === "right" || block.textHeadingAlign === "justify" ? block.textHeadingAlign : current.typography.headingAlign;
238
- current.typography.bodyAlign = block.textBodyAlign === "left" || block.textBodyAlign === "center" || block.textBodyAlign === "right" || block.textBodyAlign === "justify" ? block.textBodyAlign : current.typography.bodyAlign;
258
+ current.typography.headingAlign = isTextAlign(block.textHeadingAlign) ? block.textHeadingAlign : current.typography.headingAlign;
259
+ current.typography.bodyAlign = isTextAlign(block.textBodyAlign) ? block.textBodyAlign : current.typography.bodyAlign;
260
+ current.typography.headingSizePt = parsePoint(block.textHeadingSizePt);
261
+ current.typography.bodySizePt = parsePoint(block.textBodySizePt);
239
262
  current.typography.maxTextWidth = block.textMaxWidth === "auto" || block.textMaxWidth === "sm" || block.textMaxWidth === "md" || block.textMaxWidth === "lg" || block.textMaxWidth === "full" ? block.textMaxWidth : current.typography.maxTextWidth;
240
263
  current.typography.lineHeightPreset = block.textLineHeightPreset === "tight" || block.textLineHeightPreset === "normal" || block.textLineHeightPreset === "relaxed" ? block.textLineHeightPreset : current.typography.lineHeightPreset;
241
264
  current.typography.letterSpacingPreset = block.textLetterSpacingPreset === "tight" || block.textLetterSpacingPreset === "normal" || block.textLetterSpacingPreset === "relaxed" ? block.textLetterSpacingPreset : current.typography.letterSpacingPreset;
242
265
  current.advanced.editCopyInPanel = Boolean(block.editCopyInPanel ?? current.advanced.editCopyInPanel);
243
266
  current.advanced.customClassName = typeof block.customClassName === "string" ? block.customClassName : current.advanced.customClassName;
244
267
  current.advanced.hideOnMobile = Boolean(block.hideOnMobile ?? current.advanced.hideOnMobile);
245
- return mergeSettings(current, block.settings);
268
+ const settings = mergeSettings(current, block.settings);
269
+ if (block.blockType === "hero") {
270
+ const top = settings.layout.paddingTopPt;
271
+ const bottom = settings.layout.paddingBottomPt;
272
+ const hasLegacyLinkedHeroPadding = settings.layout.linkVerticalPadding !== false && (top === null || typeof top === "undefined" || top === 30) && (bottom === null || typeof bottom === "undefined" || bottom === 30 || bottom === 20);
273
+ if (hasLegacyLinkedHeroPadding) {
274
+ settings.layout.linkVerticalPadding = false;
275
+ settings.layout.paddingTopPt = 30;
276
+ settings.layout.paddingBottomPt = 20;
277
+ }
278
+ }
279
+ return settings;
246
280
  };
247
281
  var v2SettingsToLegacyBlock = (blockWithSettings) => {
248
282
  const settings = legacyBlockToV2Settings(blockWithSettings);
@@ -268,6 +302,8 @@ var v2SettingsToLegacyBlock = (blockWithSettings) => {
268
302
  next.contentGradientAngle = settings.appearance.contentGradientAngle;
269
303
  next.textHeadingAlign = settings.typography.headingAlign;
270
304
  next.textBodyAlign = settings.typography.bodyAlign;
305
+ next.textHeadingSizePt = settings.typography.headingSizePt;
306
+ next.textBodySizePt = settings.typography.bodySizePt;
271
307
  next.textMaxWidth = settings.typography.maxTextWidth;
272
308
  next.textLineHeightPreset = settings.typography.lineHeightPreset;
273
309
  next.textLetterSpacingPreset = settings.typography.letterSpacingPreset;
@@ -362,6 +398,7 @@ var resolveTypographyStyleFromSettings = (settings) => {
362
398
  const letterSpacing = settings.letterSpacingPreset === "tight" ? "-0.01em" : settings.letterSpacingPreset === "relaxed" ? "0.02em" : "0.01em";
363
399
  const maxWidth = settings.maxTextWidth === "sm" ? "36ch" : settings.maxTextWidth === "md" ? "52ch" : settings.maxTextWidth === "lg" ? "72ch" : settings.maxTextWidth === "full" ? "100%" : void 0;
364
400
  return {
401
+ ...typeof settings.bodySizePt === "number" && Number.isFinite(settings.bodySizePt) ? { fontSize: `${settings.bodySizePt}pt` } : {},
365
402
  letterSpacing,
366
403
  lineHeight,
367
404
  maxWidth,
@@ -531,8 +568,52 @@ var alignOptions = [
531
568
  { label: "Right", value: "right" },
532
569
  { label: "Justify", value: "justify" }
533
570
  ];
534
- var layoutFieldSet = [];
571
+ var layoutFieldSet = [
572
+ {
573
+ group: "layout",
574
+ key: "settings.layout.linkVerticalPadding",
575
+ label: "Link Top/Bottom Padding",
576
+ tags: ["spacing", "padding", "vertical"],
577
+ type: "checkbox"
578
+ },
579
+ {
580
+ group: "layout",
581
+ key: "settings.layout.paddingTopPt",
582
+ label: "Top Padding (pt)",
583
+ max: 240,
584
+ min: 0,
585
+ tags: ["spacing", "padding", "top"],
586
+ type: "number"
587
+ },
588
+ {
589
+ group: "layout",
590
+ key: "settings.layout.paddingBottomPt",
591
+ label: "Bottom Padding (pt)",
592
+ max: 240,
593
+ min: 0,
594
+ tags: ["spacing", "padding", "bottom"],
595
+ type: "number"
596
+ }
597
+ ];
535
598
  var typographyFieldSet = [
599
+ {
600
+ group: "typography",
601
+ key: "settings.typography.headingSizePt",
602
+ label: "Heading Text Size (pt)",
603
+ max: 120,
604
+ min: 6,
605
+ tags: ["text", "size", "heading", "font"],
606
+ type: "number"
607
+ },
608
+ {
609
+ group: "typography",
610
+ key: "settings.typography.bodySizePt",
611
+ label: "Body Text Size (pt)",
612
+ max: 72,
613
+ min: 6,
614
+ tags: ["text", "size", "body", "paragraph", "font"],
615
+ type: "number"
616
+ },
536
617
  {
537
618
  group: "typography",
538
619
  key: "settings.typography.headingAlign",
@@ -639,6 +720,7 @@ var mediaFieldSet = [
639
720
  ];
640
721
  var commonInspectorGroups = [
641
722
  { key: "basics", label: "Basics" },
723
+ { key: "layout", label: "Layout" },
642
724
  { key: "typography", label: "Typography" },
643
725
  { key: "media", label: "Media" },
644
726
  { key: "advanced", label: "Advanced" }
@@ -755,6 +837,13 @@ var inspectorDefinitionByBlockType = {
755
837
  { group: "basics", inlineEditable: true, key: "kicker", label: "Kicker", type: "text" },
756
838
  { group: "basics", inlineEditable: true, key: "headline", label: "Headline", type: "text" },
757
839
  { group: "basics", inlineEditable: true, key: "subheadline", label: "Subheadline", type: "textarea" },
840
+ {
841
+ group: "basics",
842
+ key: "settings.marquee.itemsText",
843
+ label: "Scrolling Bar Options",
844
+ tags: ["marquee", "scrolling", "bar", "categories"],
845
+ type: "textarea"
846
+ },
758
847
  {
759
848
  group: "basics",
760
849
  key: "variant",
@@ -924,6 +1013,69 @@ var normalizeNumber = (value, fallback) => {
924
1013
  }
925
1014
  return fallback;
926
1015
  };
1016
+ var sectionPaddingPointMap = {
1017
+ none: 0,
1018
+ sm: 17,
1019
+ md: 31,
1020
+ lg: 41
1021
+ };
1022
+ var heroDefaultPaddingTopPt = 30;
1023
+ var heroDefaultPaddingBottomPt = 20;
1024
+ var defaultTextSizePointMap = {
1025
+ cta: { body: 14, heading: 36 },
1026
+ faq: { body: 12, heading: 18 },
1027
+ featureGrid: { body: 12, heading: 36 },
1028
+ formEmbed: { body: 12, heading: 36 },
1029
+ hero: { body: 14, heading: 48 },
1030
+ media: { body: 12, heading: 36 },
1031
+ richText: { body: 12, heading: 39 },
1032
+ stats: { body: 11, heading: 30 },
1033
+ testimonials: { body: 12, heading: 36 }
1034
+ };
1035
+ var normalizeSectionPaddingY = (value) => value === "none" || value === "sm" || value === "lg" ? value : "md";
1036
+ var normalizePaddingPointValue = (value) => {
1037
+ if (typeof value === "number" && Number.isFinite(value)) {
1038
+ return Math.max(0, Math.min(240, Math.round(value)));
1039
+ }
1040
+ if (typeof value === "string" && value.trim().length > 0) {
1041
+ const parsed = Number(value);
1042
+ if (Number.isFinite(parsed)) {
1043
+ return Math.max(0, Math.min(240, Math.round(parsed)));
1044
+ }
1045
+ }
1046
+ return null;
1047
+ };
1048
+ var normalizeTextSizePointValue = (value) => {
1049
+ if (typeof value === "number" && Number.isFinite(value)) {
1050
+ return Math.max(6, Math.min(120, Math.round(value)));
1051
+ }
1052
+ if (typeof value === "string" && value.trim().length > 0) {
1053
+ const parsed = Number(value);
1054
+ if (Number.isFinite(parsed)) {
1055
+ return Math.max(6, Math.min(120, Math.round(parsed)));
1056
+ }
1057
+ }
1058
+ return null;
1059
+ };
1060
+ var getEffectiveVerticalPaddingValue = (block, key) => {
1061
+ const explicit = normalizePaddingPointValue(getByPath(block, key));
1062
+ if (explicit !== null) {
1063
+ return explicit;
1064
+ }
1065
+ const layoutSectionPaddingY = getByPath(block, "settings.layout.sectionPaddingY");
1066
+ const legacySectionPaddingY = block.sectionPaddingY;
1067
+ const inherited = block.blockType === "hero" ? key === "settings.layout.paddingBottomPt" ? heroDefaultPaddingBottomPt : heroDefaultPaddingTopPt : sectionPaddingPointMap[normalizeSectionPaddingY(layoutSectionPaddingY || legacySectionPaddingY)];
1068
+ return inherited;
1069
+ };
1070
+ var getEffectiveTextSizeValue = (block, key) => {
1071
+ const explicit = normalizeTextSizePointValue(getByPath(block, key));
1072
+ if (explicit !== null) {
1073
+ return explicit;
1074
+ }
1075
+ const blockType = typeof block.blockType === "string" ? block.blockType : "";
1076
+ const defaults = defaultTextSizePointMap[blockType] || { body: 12, heading: 36 };
1077
+ return key === "settings.typography.headingSizePt" ? defaults.heading : defaults.body;
1078
+ };
927
1079
  var getRelationID = (value) => {
928
1080
  if (typeof value === "number" || typeof value === "string") {
929
1081
  return value;
@@ -952,6 +1104,12 @@ var toMediaLibraryItem = (value) => {
952
1104
  };
953
1105
  };
954
1106
  var mediaLabel = (item) => item.filename || item.alt || `Media #${item.id}`;
1107
+ var findMediaLibraryItem = (library, id) => {
1108
+ if (id === null) {
1109
+ return null;
1110
+ }
1111
+ return library.find((item) => String(item.id) === String(id)) || null;
1112
+ };
955
1113
  var groupLabel = (key) => commonInspectorGroups.find((group) => group.key === key)?.label || key;
956
1114
  function BlockInspectorRenderer({
957
1115
  block,
@@ -972,6 +1130,21 @@ function BlockInspectorRenderer({
972
1130
  media: false,
973
1131
  typography: false
974
1132
  });
1133
+ const [supportsEyeDropper, setSupportsEyeDropper] = (0, import_react2.useState)(false);
1134
+ (0, import_react2.useEffect)(() => {
1135
+ setSupportsEyeDropper(Boolean(window.EyeDropper));
1136
+ }, []);
1137
+ const pickColorFromScreen = async (onSelect) => {
1138
+ const EyeDropper = window.EyeDropper;
1139
+ if (!EyeDropper) {
1140
+ return;
1141
+ }
1142
+ try {
1143
+ const result = await new EyeDropper().open();
1144
+ onSelect(result.sRGBHex);
1145
+ } catch {
1146
+ }
1147
+ };
975
1148
  const definition = inspectorDefinitionByBlockType[blockType];
976
1149
  const fields = definition?.fields || [];
977
1150
  const resolvedFields = (0, import_react2.useMemo)(() => {
@@ -1075,8 +1248,9 @@ function BlockInspectorRenderer({
1075
1248
  title: group.label,
1076
1249
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-builder-settings-field-list", children: [
1077
1250
  group.key === "media" ? effectiveMediaSources.map((source) => {
1078
- const selectedSourceMedia = toMediaLibraryItem(source.value);
1079
1251
  const selectedSourceMediaID = getRelationID(source.value);
1252
+ const selectedRelationMedia = toMediaLibraryItem(source.value);
1253
+ const selectedSourceMedia = selectedRelationMedia ?? findMediaLibraryItem(source.library, selectedSourceMediaID);
1080
1254
  const sourceOptions = selectedSourceMedia && !source.library.some((item) => String(item.id) === String(selectedSourceMedia.id)) ? [selectedSourceMedia, ...source.library] : source.library;
1081
1255
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1082
1256
  "div",
@@ -1086,6 +1260,26 @@ function BlockInspectorRenderer({
1086
1260
  children: [
1087
1261
  source.loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-note", children: "Loading media library..." }) : null,
1088
1262
  source.error ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-error", children: source.error }) : null,
1263
+ selectedSourceMedia?.url ? (
1264
+ // eslint-disable-next-line @next/next/no-img-element
1265
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1266
+ "img",
1267
+ {
1268
+ alt: selectedSourceMedia.alt || source.label,
1269
+ src: selectedSourceMedia.url,
1270
+ style: {
1271
+ aspectRatio: "16 / 9",
1272
+ border: "1px solid rgba(35, 51, 82, 0.14)",
1273
+ borderRadius: 10,
1274
+ display: "block",
1275
+ marginBottom: "0.7rem",
1276
+ objectFit: effectiveMedia.fit,
1277
+ objectPosition: `${effectiveMedia.positionX}% ${effectiveMedia.positionY}%`,
1278
+ width: "100%"
1279
+ }
1280
+ }
1281
+ )
1282
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-builder-settings-empty", children: "No image selected." }),
1089
1283
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1090
1284
  source.label,
1091
1285
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
@@ -1101,6 +1295,10 @@ function BlockInspectorRenderer({
1101
1295
  }
1102
1296
  )
1103
1297
  ] }),
1298
+ selectedSourceMedia?.alt ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1299
+ "Media Description",
1300
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "orion-builder-settings-input", readOnly: true, type: "text", value: selectedSourceMedia.alt })
1301
+ ] }) : 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,
1104
1302
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1105
1303
  "button",
1106
1304
  {
@@ -1157,19 +1355,20 @@ function BlockInspectorRenderer({
1157
1355
  }
1158
1356
  ) : null,
1159
1357
  (hasMediaGroupContent ? group.fields.filter((field) => !(group.key === "media" && field.key.startsWith("settings.media."))) : group.fields).map((field) => {
1160
- if (field.key === "settings.layout.paddingBottomPt" && Boolean(getByPath(block, "settings.layout.linkVerticalPadding"))) {
1358
+ if (field.key === "settings.layout.paddingBottomPt" && getByPath(block, "settings.layout.linkVerticalPadding") !== false && block.blockType !== "hero") {
1161
1359
  return null;
1162
1360
  }
1163
- if (field.key === "settings.layout.paddingRightPt" && Boolean(getByPath(block, "settings.layout.linkHorizontalPadding"))) {
1361
+ if (field.key === "settings.layout.paddingRightPt" && getByPath(block, "settings.layout.linkHorizontalPadding") !== false) {
1164
1362
  return null;
1165
1363
  }
1166
1364
  const fieldValue = getByPath(block, field.key);
1167
1365
  if (field.type === "checkbox") {
1366
+ const checked = field.key === "settings.layout.linkVerticalPadding" || field.key === "settings.layout.linkHorizontalPadding" ? typeof fieldValue === "boolean" ? fieldValue : block.blockType === "hero" ? false : true : Boolean(fieldValue);
1168
1367
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label is-checkbox", children: [
1169
1368
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1170
1369
  "input",
1171
1370
  {
1172
- checked: Boolean(fieldValue),
1371
+ checked,
1173
1372
  onChange: (event) => updateForKey(field.key, event.target.checked),
1174
1373
  type: "checkbox"
1175
1374
  }
@@ -1192,8 +1391,10 @@ function BlockInspectorRenderer({
1192
1391
  ] }, field.key);
1193
1392
  }
1194
1393
  if (field.type === "number") {
1195
- const numberValue = typeof fieldValue === "number" ? fieldValue : typeof fieldValue === "string" && fieldValue.trim().length > 0 ? Number(fieldValue) : null;
1196
- const resolvedValue = typeof numberValue === "number" && Number.isFinite(numberValue) ? numberValue : "";
1394
+ const isPaddingPointField = field.key === "settings.layout.paddingTopPt" || field.key === "settings.layout.paddingBottomPt";
1395
+ const isTextSizePointField = field.key === "settings.typography.headingSizePt" || field.key === "settings.typography.bodySizePt";
1396
+ const numberValue = isTextSizePointField ? normalizeTextSizePointValue(fieldValue) : normalizePaddingPointValue(fieldValue);
1397
+ const resolvedValue = numberValue !== null ? numberValue : isPaddingPointField ? getEffectiveVerticalPaddingValue(block, field.key) : isTextSizePointField ? getEffectiveTextSizeValue(block, field.key) : "";
1197
1398
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1198
1399
  field.label,
1199
1400
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -1237,15 +1438,35 @@ function BlockInspectorRenderer({
1237
1438
  if (field.type === "color") {
1238
1439
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
1239
1440
  field.label,
1240
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1241
- "input",
1242
- {
1243
- className: "orion-builder-settings-input is-color",
1244
- onChange: (event) => updateForKey(field.key, event.target.value),
1245
- type: "color",
1246
- value: typeof fieldValue === "string" && fieldValue.length > 0 ? fieldValue : "#ffffff"
1247
- }
1248
- )
1441
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "orion-builder-settings-color-control", children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1443
+ "input",
1444
+ {
1445
+ className: "orion-builder-settings-input is-color",
1446
+ onChange: (event) => updateForKey(field.key, event.target.value),
1447
+ type: "color",
1448
+ value: typeof fieldValue === "string" && fieldValue.length > 0 ? fieldValue : "#ffffff"
1449
+ }
1450
+ ),
1451
+ supportsEyeDropper ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1452
+ "button",
1453
+ {
1454
+ "aria-label": `Pick ${field.label} from screen`,
1455
+ className: "orion-builder-settings-eyedropper",
1456
+ "data-orion-color-eyedropper": "true",
1457
+ onClick: () => pickColorFromScreen((color) => updateForKey(field.key, color)),
1458
+ title: "Pick color from screen",
1459
+ type: "button",
1460
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1461
+ "path",
1462
+ {
1463
+ d: "M14.7 4.3a2.4 2.4 0 0 1 3.4 0l1.6 1.6a2.4 2.4 0 0 1 0 3.4l-1.2 1.2 1 1a1 1 0 1 1-1.4 1.4l-1-1-8.9 8.9H4v-4.2l8.9-8.9-1-1a1 1 0 0 1 1.4-1.4l1 1 1.4-1.4Zm-.4 4.8-8.3 8.3V19h1.6l8.3-8.3-1.6-1.6Z",
1464
+ fill: "currentColor"
1465
+ }
1466
+ ) })
1467
+ }
1468
+ ) : null
1469
+ ] })
1249
1470
  ] }, field.key);
1250
1471
  }
1251
1472
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "orion-builder-settings-label", children: [
@@ -2249,8 +2470,8 @@ function renderSimpleBlockPreview(args) {
2249
2470
  getImagePresentationStyle: getImagePresentationStyle2,
2250
2471
  index,
2251
2472
  isBlockUploadTarget,
2252
- normalizeImageCornerStyle: normalizeImageCornerStyle2,
2253
- normalizeImageFit: normalizeImageFit2,
2473
+ normalizeImageCornerStyle: normalizeImageCornerStyle3,
2474
+ normalizeImageFit: normalizeImageFit3,
2254
2475
  normalizeText: normalizeText3,
2255
2476
  onDropAt,
2256
2477
  renderWithSectionShell,
@@ -2434,8 +2655,8 @@ function renderSimpleBlockPreview(args) {
2434
2655
  const imagePositionX = parseOptionalPercentNumber(mediaSettings.positionX ?? block?.imagePositionX);
2435
2656
  const imagePositionY = parseOptionalPercentNumber(mediaSettings.positionY ?? block?.imagePositionY);
2436
2657
  const imageStyle = getImagePresentationStyle2({
2437
- cornerStyle: normalizeImageCornerStyle2(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2438
- fit: normalizeImageFit2(mediaSettings.fit ?? block?.imageFit),
2658
+ cornerStyle: normalizeImageCornerStyle3(mediaSettings.cornerStyle ?? block?.imageCornerStyle),
2659
+ fit: normalizeImageFit3(mediaSettings.fit ?? block?.imageFit),
2439
2660
  positionX: imagePositionX,
2440
2661
  positionY: imagePositionY
2441
2662
  });
@@ -2965,6 +3186,76 @@ var normalizeNumber2 = (value, fallback) => {
2965
3186
  return fallback;
2966
3187
  };
2967
3188
  var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
3189
+ var getRelationID2 = (value) => {
3190
+ if (typeof value === "number" || typeof value === "string") {
3191
+ return value;
3192
+ }
3193
+ if (!isRecord5(value)) {
3194
+ return null;
3195
+ }
3196
+ const id = value.id;
3197
+ return typeof id === "number" || typeof id === "string" ? id : null;
3198
+ };
3199
+ var toMediaLibraryItem2 = (value) => {
3200
+ if (!isRecord5(value)) {
3201
+ return null;
3202
+ }
3203
+ const id = getRelationID2(value);
3204
+ if (id === null) {
3205
+ return null;
3206
+ }
3207
+ const filename = typeof value.filename === "string" ? value.filename : "";
3208
+ const url = typeof value.url === "string" && value.url.length > 0 ? value.url : filename ? `/api/media/file/${encodeURIComponent(filename)}` : "";
3209
+ return {
3210
+ alt: typeof value.alt === "string" ? value.alt : "",
3211
+ filename,
3212
+ id,
3213
+ url
3214
+ };
3215
+ };
3216
+ var mediaFromImageURL = (url, fallbackLabel) => {
3217
+ if (typeof url !== "string" || url.trim().length === 0) {
3218
+ return null;
3219
+ }
3220
+ const normalizedURL = url.trim();
3221
+ const filename = normalizedURL.split("/").filter(Boolean).pop() || fallbackLabel;
3222
+ return {
3223
+ alt: fallbackLabel,
3224
+ filename,
3225
+ id: `url:${normalizedURL}`,
3226
+ url: normalizedURL
3227
+ };
3228
+ };
3229
+ var mediaLabel2 = (item) => item.filename || item.alt || `Media #${item.id}`;
3230
+ var findMediaLibraryItem2 = (library, id) => {
3231
+ if (id === null) {
3232
+ return null;
3233
+ }
3234
+ return library.find((item) => String(item.id) === String(id)) || null;
3235
+ };
3236
+ var normalizeImageFit = (value) => value === "contain" ? "contain" : "cover";
3237
+ var normalizeImageCornerStyle = (value) => value === "square" ? "square" : "rounded";
3238
+ var getItemMediaSettings = (item) => {
3239
+ const settings = isRecord5(item.settings) ? item.settings : {};
3240
+ const media = isRecord5(settings.media) ? settings.media : {};
3241
+ return {
3242
+ cornerStyle: normalizeImageCornerStyle(media.cornerStyle ?? item.imageCornerStyle),
3243
+ fit: normalizeImageFit(media.fit ?? item.imageFit),
3244
+ height: (() => {
3245
+ const value = media.height ?? item.imageHeight;
3246
+ if (typeof value === "number" && Number.isFinite(value)) {
3247
+ return value;
3248
+ }
3249
+ if (typeof value === "string" && value.trim().length > 0) {
3250
+ const parsed = Number(value);
3251
+ return Number.isFinite(parsed) ? parsed : null;
3252
+ }
3253
+ return null;
3254
+ })(),
3255
+ positionX: clamp(normalizeNumber2(media.positionX ?? item.imagePositionX, 50), 0, 100),
3256
+ positionY: clamp(normalizeNumber2(media.positionY ?? item.imagePositionY, 50), 0, 100)
3257
+ };
3258
+ };
2968
3259
  var hasQueryMatch = (query, ...values) => {
2969
3260
  const normalized = query.trim().toLowerCase();
2970
3261
  if (!normalized) {
@@ -3011,18 +3302,148 @@ var bulletsToTextareaValue = (value) => {
3011
3302
  return value.map((item) => isRecord5(item) && typeof item.label === "string" ? item.label.trim() : "").filter(Boolean).join("\n");
3012
3303
  };
3013
3304
  var textareaValueToBullets = (value) => value.split("\n").map((line) => line.trim()).filter(Boolean).map((label) => ({ label }));
3305
+ function ItemMediaControl({
3306
+ fieldName,
3307
+ imageURLFieldName,
3308
+ item,
3309
+ itemIndex,
3310
+ label,
3311
+ mediaLibrary,
3312
+ mediaLibraryError,
3313
+ mediaLibraryLoading,
3314
+ maxHeight,
3315
+ minHeight,
3316
+ onRemoveItemMedia,
3317
+ onSelectItemMedia,
3318
+ onUpdateItemField,
3319
+ onUpdateItemMediaPresentation,
3320
+ onUploadItemMedia,
3321
+ searchQuery,
3322
+ uploadDisabled,
3323
+ uploadLabel,
3324
+ uploading
3325
+ }) {
3326
+ const directImageURL = normalizeText(item[imageURLFieldName]);
3327
+ const selectedRelationMedia = toMediaLibraryItem2(item[fieldName]) ?? mediaFromImageURL(directImageURL, label);
3328
+ const selectedMediaID = getRelationID2(item[fieldName]) ?? selectedRelationMedia?.id ?? null;
3329
+ const selectedMedia = selectedRelationMedia ?? findMediaLibraryItem2(mediaLibrary, selectedMediaID);
3330
+ const previewURL = selectedMedia?.url || directImageURL;
3331
+ const mediaSettings = getItemMediaSettings(item);
3332
+ const sourceOptions = selectedMedia && !mediaLibrary.some((libraryItem) => String(libraryItem.id) === String(selectedMedia.id)) ? [selectedMedia, ...mediaLibrary] : mediaLibrary;
3333
+ if (!hasQueryMatch(searchQuery, label, "image", "media", "photo", "picture", "url", "fit", "crop", "height")) {
3334
+ return null;
3335
+ }
3336
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-item-card", style: { padding: "0.56rem" }, children: [
3337
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-label", children: label }),
3338
+ previewURL ? (
3339
+ // eslint-disable-next-line @next/next/no-img-element
3340
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3341
+ "img",
3342
+ {
3343
+ alt: selectedMedia?.alt || label,
3344
+ src: previewURL,
3345
+ style: {
3346
+ aspectRatio: "16 / 9",
3347
+ border: "1px solid rgba(35, 51, 82, 0.14)",
3348
+ borderRadius: mediaSettings.cornerStyle === "square" ? 4 : 10,
3349
+ display: "block",
3350
+ objectFit: mediaSettings.fit,
3351
+ objectPosition: `${mediaSettings.positionX}% ${mediaSettings.positionY}%`,
3352
+ width: "100%"
3353
+ }
3354
+ }
3355
+ )
3356
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-empty", children: "No image selected." }),
3357
+ mediaLibraryLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-note", children: "Loading media library..." }) : null,
3358
+ mediaLibraryError ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-error", children: mediaLibraryError }) : null,
3359
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3360
+ "Media Library Image",
3361
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3362
+ "select",
3363
+ {
3364
+ className: "orion-builder-settings-input",
3365
+ onChange: (event) => onSelectItemMedia?.(itemIndex, fieldName, event.target.value),
3366
+ value: selectedMediaID !== null ? String(selectedMediaID) : "",
3367
+ children: [
3368
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: "", children: "No image" }),
3369
+ sourceOptions.map((libraryItem) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("option", { value: String(libraryItem.id), children: mediaLabel2(libraryItem) }, String(libraryItem.id)))
3370
+ ]
3371
+ }
3372
+ )
3373
+ ] }),
3374
+ selectedMedia?.alt ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3375
+ "Media Description",
3376
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { className: "orion-builder-settings-input", readOnly: true, type: "text", value: selectedMedia.alt })
3377
+ ] }) : 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,
3378
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3379
+ "button",
3380
+ {
3381
+ className: "orion-builder-settings-inline-btn",
3382
+ disabled: selectedMediaID === null,
3383
+ onClick: () => {
3384
+ onRemoveItemMedia?.(itemIndex, fieldName);
3385
+ onUpdateItemField(itemIndex, imageURLFieldName, "");
3386
+ },
3387
+ type: "button",
3388
+ children: "Remove Image"
3389
+ }
3390
+ ),
3391
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
3392
+ uploadLabel,
3393
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3394
+ "input",
3395
+ {
3396
+ accept: "image/*",
3397
+ className: "orion-builder-settings-input",
3398
+ disabled: uploadDisabled,
3399
+ onChange: (event) => {
3400
+ const file = event.currentTarget.files?.[0];
3401
+ if (file) {
3402
+ onUploadItemMedia?.(itemIndex, fieldName, file);
3403
+ }
3404
+ event.currentTarget.value = "";
3405
+ },
3406
+ type: "file"
3407
+ }
3408
+ )
3409
+ ] }),
3410
+ uploading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "orion-builder-settings-note", children: "Uploading image..." }) : null,
3411
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3412
+ ImageControls,
3413
+ {
3414
+ cornerStyle: mediaSettings.cornerStyle,
3415
+ fit: mediaSettings.fit,
3416
+ height: mediaSettings.height,
3417
+ maxHeight,
3418
+ minHeight,
3419
+ onChange: (field, value) => onUpdateItemMediaPresentation?.(itemIndex, field, value),
3420
+ positionX: mediaSettings.positionX,
3421
+ positionY: mediaSettings.positionY
3422
+ }
3423
+ )
3424
+ ] });
3425
+ }
3014
3426
  function ArrayItemsEditor({
3015
3427
  blockType,
3016
3428
  expandedItemIndex,
3017
3429
  items,
3430
+ mediaLibrary = [],
3431
+ mediaLibraryError = "",
3432
+ mediaLibraryLoading = false,
3018
3433
  mode,
3019
3434
  onAddItem,
3435
+ onRemoveItemMedia,
3020
3436
  onRemoveItem,
3437
+ onSelectItemMedia,
3021
3438
  onToggleItem,
3022
3439
  onUpdateItemField,
3440
+ onUpdateItemMediaPresentation,
3023
3441
  onUpdateItemSetting,
3442
+ onUploadItemMedia,
3024
3443
  searchQuery,
3025
- showInlineCopyFields
3444
+ showInlineCopyFields,
3445
+ uploadDisabled = false,
3446
+ isItemMediaUploading
3026
3447
  }) {
3027
3448
  const config = blockConfig[blockType];
3028
3449
  const normalizedQuery = searchQuery.trim().toLowerCase();
@@ -3172,11 +3593,30 @@ function ArrayItemsEditor({
3172
3593
  }
3173
3594
  )
3174
3595
  ] }) : null,
3175
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-note", children: [
3176
- "Select this item and use the ",
3177
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Media" }),
3178
- " group above for image source and presentation."
3179
- ] })
3596
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3597
+ ItemMediaControl,
3598
+ {
3599
+ fieldName: "media",
3600
+ imageURLFieldName: "imageURL",
3601
+ item,
3602
+ itemIndex,
3603
+ label: "Feature Image",
3604
+ maxHeight: 600,
3605
+ mediaLibrary,
3606
+ mediaLibraryError,
3607
+ mediaLibraryLoading,
3608
+ minHeight: 40,
3609
+ onRemoveItemMedia,
3610
+ onSelectItemMedia,
3611
+ onUpdateItemField,
3612
+ onUpdateItemMediaPresentation,
3613
+ onUploadItemMedia,
3614
+ searchQuery: normalizedQuery,
3615
+ uploadDisabled,
3616
+ uploadLabel: "Upload Feature Image",
3617
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3618
+ }
3619
+ )
3180
3620
  ] }) : null,
3181
3621
  blockType === "logoWall" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3182
3622
  showInlineCopyFields ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
@@ -3203,11 +3643,30 @@ function ArrayItemsEditor({
3203
3643
  }
3204
3644
  )
3205
3645
  ] }) : null,
3206
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-note", children: [
3207
- "Select this item and use the ",
3208
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Media" }),
3209
- " group above for image source and presentation."
3210
- ] })
3646
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3647
+ ItemMediaControl,
3648
+ {
3649
+ fieldName: "media",
3650
+ imageURLFieldName: "imageURL",
3651
+ item,
3652
+ itemIndex,
3653
+ label: "Logo Image",
3654
+ maxHeight: 200,
3655
+ mediaLibrary,
3656
+ mediaLibraryError,
3657
+ mediaLibraryLoading,
3658
+ minHeight: 24,
3659
+ onRemoveItemMedia,
3660
+ onSelectItemMedia,
3661
+ onUpdateItemField,
3662
+ onUpdateItemMediaPresentation,
3663
+ onUploadItemMedia,
3664
+ searchQuery: normalizedQuery,
3665
+ uploadDisabled,
3666
+ uploadLabel: "Upload Logo Image",
3667
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "media"))
3668
+ }
3669
+ )
3211
3670
  ] }) : null,
3212
3671
  blockType === "beforeAfter" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
3213
3672
  showInlineCopyFields ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
@@ -3235,11 +3694,54 @@ function ArrayItemsEditor({
3235
3694
  )
3236
3695
  ] })
3237
3696
  ] }) : null,
3238
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "orion-builder-settings-note", children: [
3239
- "Select this item and use the ",
3240
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { children: "Media" }),
3241
- " group above for before/after image source and presentation."
3242
- ] })
3697
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3698
+ ItemMediaControl,
3699
+ {
3700
+ fieldName: "beforeMedia",
3701
+ imageURLFieldName: "beforeImageURL",
3702
+ item,
3703
+ itemIndex,
3704
+ label: "Before Image",
3705
+ maxHeight: 600,
3706
+ mediaLibrary,
3707
+ mediaLibraryError,
3708
+ mediaLibraryLoading,
3709
+ minHeight: 60,
3710
+ onRemoveItemMedia,
3711
+ onSelectItemMedia,
3712
+ onUpdateItemField,
3713
+ onUpdateItemMediaPresentation,
3714
+ onUploadItemMedia,
3715
+ searchQuery: normalizedQuery,
3716
+ uploadDisabled,
3717
+ uploadLabel: "Upload Before Image",
3718
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "beforeMedia"))
3719
+ }
3720
+ ),
3721
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3722
+ ItemMediaControl,
3723
+ {
3724
+ fieldName: "afterMedia",
3725
+ imageURLFieldName: "afterImageURL",
3726
+ item,
3727
+ itemIndex,
3728
+ label: "After Image",
3729
+ maxHeight: 600,
3730
+ mediaLibrary,
3731
+ mediaLibraryError,
3732
+ mediaLibraryLoading,
3733
+ minHeight: 60,
3734
+ onRemoveItemMedia,
3735
+ onSelectItemMedia,
3736
+ onUpdateItemField,
3737
+ onUpdateItemMediaPresentation,
3738
+ onUploadItemMedia,
3739
+ searchQuery: normalizedQuery,
3740
+ uploadDisabled,
3741
+ uploadLabel: "Upload After Image",
3742
+ uploading: Boolean(isItemMediaUploading?.(itemIndex, "afterMedia"))
3743
+ }
3744
+ )
3243
3745
  ] }) : null,
3244
3746
  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: [
3245
3747
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { className: "orion-builder-settings-label", children: [
@@ -3404,6 +3906,19 @@ var resolveMedia = (value) => {
3404
3906
  }
3405
3907
  return null;
3406
3908
  };
3909
+ var mediaFromImageURL2 = (url, fallbackLabel = "Current image") => {
3910
+ if (typeof url !== "string" || url.trim().length === 0) {
3911
+ return null;
3912
+ }
3913
+ const normalizedURL = url.trim();
3914
+ const filename = normalizedURL.split("/").filter(Boolean).pop() || fallbackLabel;
3915
+ return {
3916
+ alt: fallbackLabel,
3917
+ filename,
3918
+ id: `url:${normalizedURL}`,
3919
+ url: normalizedURL
3920
+ };
3921
+ };
3407
3922
  var resolveMediaWithURL = (value, imageURL, alt) => {
3408
3923
  const resolved = resolveMedia(value);
3409
3924
  if (resolved?.url) {
@@ -3445,8 +3960,8 @@ var resolveBuilderMediumHeroHeight = (topViewportHeight) => {
3445
3960
  }
3446
3961
  return "50svh";
3447
3962
  };
3448
- var normalizeImageFit = (value) => normalizeHeroImageFit(value);
3449
- var normalizeImageCornerStyle = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3963
+ var normalizeImageFit2 = (value) => normalizeHeroImageFit(value);
3964
+ var normalizeImageCornerStyle2 = (value, legacyFitValue) => normalizeHeroImageCornerStyle(value, legacyFitValue);
3450
3965
  var positionPercent = (value, fit) => {
3451
3966
  const resolved = fit === "cover" && (value === "left" || value === "right") ? "center" : value;
3452
3967
  switch (resolved) {
@@ -3615,6 +4130,8 @@ var sectionPaddingMap = {
3615
4130
  md: "2.6rem",
3616
4131
  lg: "3.4rem"
3617
4132
  };
4133
+ var heroDefaultPaddingTop = "30pt";
4134
+ var heroDefaultPaddingBottom = "20pt";
3618
4135
  var quickAddBlockTypes = [
3619
4136
  "hero",
3620
4137
  "featureGrid",
@@ -3878,6 +4395,12 @@ function getThemeColorOverride(tokens, colorKey) {
3878
4395
  const normalized = value.trim();
3879
4396
  return normalized.length > 0 ? normalized : null;
3880
4397
  }
4398
+ function normalizeCustomClassName(value) {
4399
+ if (typeof value !== "string") {
4400
+ return "";
4401
+ }
4402
+ return value.split(/\s+/).map((className) => className.trim()).filter(Boolean).join(" ");
4403
+ }
3881
4404
  var sectionStyleFromBlock = (block, pageDefaults) => {
3882
4405
  const settings = isRecord6(block.settings) ? block.settings : {};
3883
4406
  const layoutSettings = isRecord6(settings.layout) ? settings.layout : {};
@@ -3963,15 +4486,15 @@ var sectionStyleFromBlock = (block, pageDefaults) => {
3963
4486
  } : {},
3964
4487
  sectionClass: `orion-builder-shell is-${pageDefaults.pageWidthDefault} padx-${resolvedSectionPaddingX}`,
3965
4488
  sectionInnerStyle: {
3966
- paddingBottom: customPaddingBottom === null ? sectionPaddingMap[sectionPaddingY] : `${customPaddingBottom}pt`,
4489
+ paddingBottom: customPaddingBottom === null ? block.blockType === "hero" ? heroDefaultPaddingBottom : sectionPaddingMap[sectionPaddingY] : `${customPaddingBottom}pt`,
3967
4490
  paddingLeft: customPaddingLeft === null ? void 0 : `${customPaddingLeft}pt`,
3968
4491
  paddingRight: customPaddingRight === null ? void 0 : `${customPaddingRight}pt`,
3969
- paddingTop: customPaddingTop === null ? sectionPaddingMap[sectionPaddingY] : `${customPaddingTop}pt`
4492
+ paddingTop: customPaddingTop === null ? block.blockType === "hero" ? heroDefaultPaddingTop : sectionPaddingMap[sectionPaddingY] : `${customPaddingTop}pt`
3970
4493
  },
3971
4494
  sectionStyle: sectionMode === "color" ? { background: sectionColor } : sectionMode === "gradient" ? { background: sectionGradient } : block.blockType === "hero" ? { background: "transparent" } : {}
3972
4495
  };
3973
4496
  };
3974
- function getRelationID2(value) {
4497
+ function getRelationID3(value) {
3975
4498
  if (typeof value === "number" || typeof value === "string") {
3976
4499
  return value;
3977
4500
  }
@@ -4004,7 +4527,7 @@ function extractUploadedMedia(value) {
4004
4527
  if (!candidate || typeof candidate !== "object") {
4005
4528
  return null;
4006
4529
  }
4007
- const id = getRelationID2(candidate);
4530
+ const id = getRelationID3(candidate);
4008
4531
  if (id === null) {
4009
4532
  return null;
4010
4533
  }
@@ -4015,11 +4538,11 @@ function extractUploadedMedia(value) {
4015
4538
  url: typeof candidate.url === "string" ? candidate.url : ""
4016
4539
  };
4017
4540
  }
4018
- function toMediaLibraryItem2(value) {
4541
+ function toMediaLibraryItem3(value) {
4019
4542
  if (!value || typeof value !== "object") {
4020
4543
  return null;
4021
4544
  }
4022
- const id = getRelationID2(value);
4545
+ const id = getRelationID3(value);
4023
4546
  if (id === null) {
4024
4547
  return null;
4025
4548
  }
@@ -4152,7 +4675,7 @@ function BuilderPageEditor({
4152
4675
  const selectedBlockAdvancedSettings = isRecord6(selectedBlockSettings.advanced) ? selectedBlockSettings.advanced : {};
4153
4676
  const isArrayItemBlockSelected = selectedType === "featureGrid" || selectedType === "logoWall" || selectedType === "beforeAfter" || selectedType === "stats" || selectedType === "faq" || selectedType === "testimonials";
4154
4677
  const selectedBlockHasMediaSource = selectedType === "hero" || selectedType === "media";
4155
- const selectedBlockMediaValue = selectedType === "hero" ? selectedBlock?.media : selectedType === "media" ? selectedBlock?.image : null;
4678
+ const selectedBlockMediaValue = selectedType === "hero" ? selectedBlock?.media ?? mediaFromImageURL2(selectedBlock?.backgroundImageURL, "Hero image") : selectedType === "media" ? selectedBlock?.image ?? mediaFromImageURL2(selectedBlock?.imageURL, "Section image") : null;
4156
4679
  const selectedItemRecord = typeof selectedItemIndex === "number" && selectedItemIndex >= 0 && selectedItemIndex < selectedItems.length && isRecord6(selectedItems[selectedItemIndex]) ? selectedItems[selectedItemIndex] : null;
4157
4680
  const editCopyInPanelEnabled = Boolean(selectedBlockAdvancedSettings.editCopyInPanel);
4158
4681
  const isBlockUploadTarget = (blockIndex, kind) => selectedIndex === blockIndex && uploadingTarget?.kind === kind;
@@ -4197,7 +4720,7 @@ function BuilderPageEditor({
4197
4720
  }
4198
4721
  const json = await response.json();
4199
4722
  const docs = Array.isArray(json.docs) ? json.docs : [];
4200
- const items = docs.map((doc2) => toMediaLibraryItem2(doc2)).filter((item) => item !== null);
4723
+ const items = docs.map((doc2) => toMediaLibraryItem3(doc2)).filter((item) => item !== null);
4201
4724
  setMediaLibrary(items);
4202
4725
  } catch (error) {
4203
4726
  setMediaLibraryError(error instanceof Error ? error.message : "Could not load media library.");
@@ -4551,7 +5074,7 @@ function BuilderPageEditor({
4551
5074
  const nextLayout = cloneBlockLayout(layout);
4552
5075
  const block = nextLayout[selectedIndex];
4553
5076
  if (target.kind === "hero") {
4554
- const uploadedItem = toMediaLibraryItem2(uploaded);
5077
+ const uploadedItem = toMediaLibraryItem3(uploaded);
4555
5078
  nextLayout[selectedIndex] = {
4556
5079
  ...block,
4557
5080
  backgroundImageURL: uploadedItem?.url || normalizeText2(uploaded.url),
@@ -4615,13 +5138,13 @@ function BuilderPageEditor({
4615
5138
  const nextBlock = { ...migrateBlockToSettingsV2(block) };
4616
5139
  const blockType = normalizeText2(nextBlock.blockType);
4617
5140
  if (blockType === "hero") {
4618
- const mediaID = getRelationID2(nextBlock.media);
5141
+ const mediaID = getRelationID3(nextBlock.media);
4619
5142
  if (mediaID !== null) {
4620
5143
  nextBlock.media = mediaID;
4621
5144
  }
4622
5145
  }
4623
5146
  if (blockType === "media") {
4624
- const imageID = getRelationID2(nextBlock.image);
5147
+ const imageID = getRelationID3(nextBlock.image);
4625
5148
  if (imageID !== null) {
4626
5149
  nextBlock.image = imageID;
4627
5150
  }
@@ -4633,15 +5156,15 @@ function BuilderPageEditor({
4633
5156
  return rawItem;
4634
5157
  }
4635
5158
  const nextItem = { ...rawItem };
4636
- const mediaID = getRelationID2(nextItem.media);
5159
+ const mediaID = getRelationID3(nextItem.media);
4637
5160
  if (mediaID !== null) {
4638
5161
  nextItem.media = mediaID;
4639
5162
  }
4640
- const beforeMediaID = getRelationID2(nextItem.beforeMedia);
5163
+ const beforeMediaID = getRelationID3(nextItem.beforeMedia);
4641
5164
  if (beforeMediaID !== null) {
4642
5165
  nextItem.beforeMedia = beforeMediaID;
4643
5166
  }
4644
- const afterMediaID = getRelationID2(nextItem.afterMedia);
5167
+ const afterMediaID = getRelationID3(nextItem.afterMedia);
4645
5168
  if (afterMediaID !== null) {
4646
5169
  nextItem.afterMedia = afterMediaID;
4647
5170
  }
@@ -4697,6 +5220,7 @@ function BuilderPageEditor({
4697
5220
  const inheritProjectStyles = options?.inheritProjectStyles === true;
4698
5221
  const shell = sectionStyleFromBlock(block, pageDefaults);
4699
5222
  const blockSettings = isRecord6(block.settings) ? block.settings : {};
5223
+ const blockAdvanced = isRecord6(blockSettings.advanced) ? blockSettings.advanced : {};
4700
5224
  const blockTypography = isRecord6(blockSettings.typography) ? blockSettings.typography : {};
4701
5225
  const blockTheme = isRecord6(blockSettings.theme) ? blockSettings.theme : {};
4702
5226
  const heroBackgroundColor = normalizeText2(block.backgroundColor).trim();
@@ -4704,23 +5228,35 @@ function BuilderPageEditor({
4704
5228
  const heroMedia = block.blockType === "hero" ? resolveMedia(block.media) : null;
4705
5229
  const heroHasImage = block.blockType === "hero" && Boolean(heroBackgroundImageURL || heroMedia?.url);
4706
5230
  const heroTextColor = block.blockType === "hero" ? heroHasImage ? "#ffffff" : heroBackgroundColor ? getReadableTextColor(heroBackgroundColor, resolvedThemeTokens.colors.headingText, "#ffffff") : resolvedThemeTokens.colors.headingText : null;
5231
+ const bodyAlign = blockTypography.bodyAlign === "center" || blockTypography.bodyAlign === "justify" || blockTypography.bodyAlign === "right" ? blockTypography.bodyAlign : "left";
4707
5232
  const typographyStyle = resolveTypographyStyleFromSettings({
4708
- bodyAlign: blockTypography.bodyAlign === "center" || blockTypography.bodyAlign === "justify" || blockTypography.bodyAlign === "right" ? blockTypography.bodyAlign : "left",
5233
+ bodyAlign,
4709
5234
  letterSpacingPreset: blockTypography.letterSpacingPreset === "tight" || blockTypography.letterSpacingPreset === "relaxed" ? blockTypography.letterSpacingPreset : "normal",
4710
5235
  lineHeightPreset: blockTypography.lineHeightPreset === "tight" || blockTypography.lineHeightPreset === "relaxed" ? blockTypography.lineHeightPreset : "normal",
5236
+ bodySizePt: typeof blockTypography.bodySizePt === "number" && Number.isFinite(blockTypography.bodySizePt) ? blockTypography.bodySizePt : null,
4711
5237
  maxTextWidth: blockTypography.maxTextWidth === "sm" || blockTypography.maxTextWidth === "md" || blockTypography.maxTextWidth === "lg" || blockTypography.maxTextWidth === "full" ? blockTypography.maxTextWidth : "auto"
4712
5238
  });
4713
5239
  const headingAlign = blockTypography.headingAlign === "center" || blockTypography.headingAlign === "justify" || blockTypography.headingAlign === "right" ? blockTypography.headingAlign : "left";
4714
5240
  const headingColor = getThemeColorOverride(blockTheme, "headingText") || getThemeColorOverride(pageDefaults.themePage, "headingText") || getThemeColorOverride(siteThemeTokens, "headingText") || heroTextColor || resolvedThemeTokens.colors.headingText;
4715
5241
  const bodyColor = getThemeColorOverride(blockTheme, "bodyText") || getThemeColorOverride(pageDefaults.themePage, "bodyText") || getThemeColorOverride(siteThemeTokens, "bodyText") || heroTextColor || resolvedThemeTokens.colors.bodyText;
4716
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("section", { className: `${className} ${shell.sectionClass}`, style: shell.sectionStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: shell.sectionInnerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
5242
+ const customClassName = normalizeCustomClassName(blockAdvanced.customClassName ?? block.customClassName);
5243
+ const hideOnMobile = Boolean(blockAdvanced.hideOnMobile ?? block.hideOnMobile);
5244
+ const sectionClassName = [
5245
+ className,
5246
+ shell.sectionClass,
5247
+ customClassName,
5248
+ hideOnMobile ? "orion-builder-hide-mobile" : ""
5249
+ ].filter(Boolean).join(" ");
5250
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("section", { className: sectionClassName, style: shell.sectionStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: shell.sectionInnerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
4717
5251
  "div",
4718
5252
  {
4719
5253
  className: shell.contentClass,
4720
5254
  style: {
4721
5255
  ...shell.contentStyle,
5256
+ "--orion-builder-heading-align": headingAlign,
5257
+ "--orion-builder-body-align": bodyAlign,
5258
+ "--orion-builder-actions-align": bodyAlign === "center" ? "center" : bodyAlign === "right" ? "flex-end" : "flex-start",
4722
5259
  ...inheritProjectStyles ? {} : {
4723
- "--orion-builder-heading-align": headingAlign,
4724
5260
  "--orion-builder-heading-color": headingColor,
4725
5261
  color: bodyColor,
4726
5262
  ...typographyStyle
@@ -5207,8 +5743,8 @@ function BuilderPageEditor({
5207
5743
  }
5208
5744
  ] : [];
5209
5745
  const selectedMediaImageControls = selectedItemRecord && typeof selectedItemIndex === "number" && (selectedType === "featureGrid" || selectedType === "logoWall" || selectedType === "beforeAfter") ? {
5210
- cornerStyle: normalizeImageCornerStyle(selectedItemRecord.imageCornerStyle),
5211
- fit: normalizeImageFit(selectedItemRecord.imageFit),
5746
+ cornerStyle: normalizeImageCornerStyle2(selectedItemRecord.imageCornerStyle),
5747
+ fit: normalizeImageFit2(selectedItemRecord.imageFit),
5212
5748
  height: (() => {
5213
5749
  if (typeof selectedItemRecord.imageHeight === "number" && Number.isFinite(selectedItemRecord.imageHeight)) {
5214
5750
  return selectedItemRecord.imageHeight;
@@ -5683,8 +6219,8 @@ function BuilderPageEditor({
5683
6219
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5684
6220
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5685
6221
  const itemImageStyle = getImagePresentationStyle({
5686
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5687
- fit: normalizeImageFit(itemRecord?.imageFit),
6222
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6223
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5688
6224
  positionX: itemPositionX,
5689
6225
  positionY: itemPositionY
5690
6226
  });
@@ -5801,8 +6337,8 @@ function BuilderPageEditor({
5801
6337
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
5802
6338
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
5803
6339
  const itemImageStyle = getImagePresentationStyle({
5804
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
5805
- fit: normalizeImageFit(itemRecord?.imageFit),
6340
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6341
+ fit: normalizeImageFit2(itemRecord?.imageFit),
5806
6342
  positionX: itemPositionX,
5807
6343
  positionY: itemPositionY
5808
6344
  });
@@ -6008,8 +6544,8 @@ function BuilderPageEditor({
6008
6544
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
6009
6545
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
6010
6546
  const imageStyle = getImagePresentationStyle({
6011
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
6012
- fit: normalizeImageFit(itemRecord?.imageFit),
6547
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6548
+ fit: normalizeImageFit2(itemRecord?.imageFit),
6013
6549
  positionX: itemPositionX,
6014
6550
  positionY: itemPositionY,
6015
6551
  roundedRadius: 10
@@ -6122,8 +6658,8 @@ function BuilderPageEditor({
6122
6658
  const itemPositionX = parseOptionalPercentNumber2(itemRecord?.imagePositionX);
6123
6659
  const itemPositionY = parseOptionalPercentNumber2(itemRecord?.imagePositionY);
6124
6660
  const imageStyle = getImagePresentationStyle({
6125
- cornerStyle: normalizeImageCornerStyle(itemRecord?.imageCornerStyle),
6126
- fit: normalizeImageFit(itemRecord?.imageFit),
6661
+ cornerStyle: normalizeImageCornerStyle2(itemRecord?.imageCornerStyle),
6662
+ fit: normalizeImageFit2(itemRecord?.imageFit),
6127
6663
  positionX: itemPositionX,
6128
6664
  positionY: itemPositionY
6129
6665
  });
@@ -6429,8 +6965,8 @@ function BuilderPageEditor({
6429
6965
  getImagePresentationStyle,
6430
6966
  index,
6431
6967
  isBlockUploadTarget,
6432
- normalizeImageCornerStyle,
6433
- normalizeImageFit,
6968
+ normalizeImageCornerStyle: normalizeImageCornerStyle2,
6969
+ normalizeImageFit: normalizeImageFit2,
6434
6970
  normalizeText: normalizeText2,
6435
6971
  onDropAt,
6436
6972
  renderWithSectionShell,
@@ -6659,23 +7195,32 @@ function BuilderPageEditor({
6659
7195
  blockType: selectedType,
6660
7196
  expandedItemIndex,
6661
7197
  items: selectedItems,
7198
+ isItemMediaUploading: isSelectedItemMediaUploading,
7199
+ mediaLibrary,
7200
+ mediaLibraryError,
7201
+ mediaLibraryLoading,
6662
7202
  mode: settingsPanelMode,
6663
7203
  onAddItem: appendDefaultItemToSelected,
6664
7204
  onRemoveItem: (itemIndex) => removeItemFromSelected("items", itemIndex),
7205
+ onRemoveItemMedia: (itemIndex, fieldName) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, ""),
7206
+ onSelectItemMedia: (itemIndex, fieldName, mediaID) => setSelectedItemMediaFieldFromLibrary(itemIndex, fieldName, mediaID),
6665
7207
  onToggleItem: toggleSelectedItem,
6666
7208
  onUpdateItemField: (itemIndex, fieldName, value) => updateArrayItemField(selectedIndex ?? 0, "items", itemIndex, fieldName, value),
7209
+ onUpdateItemMediaPresentation: (itemIndex, fieldName, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, `media.${fieldName}`, value),
6667
7210
  onUpdateItemSetting: (itemIndex, path, value) => updateArrayItemSettingField(selectedIndex ?? 0, "items", itemIndex, path, value),
7211
+ onUploadItemMedia: uploadItemMediaFromV2,
6668
7212
  searchQuery: settingsSearchQuery,
6669
- showInlineCopyFields: editCopyInPanelEnabled
7213
+ showInlineCopyFields: editCopyInPanelEnabled,
7214
+ uploadDisabled: uploadingTarget !== null
6670
7215
  }
6671
7216
  ) : null,
6672
7217
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { style: sidebarLabelStyle, children: [
6673
- "Upload Alt Text",
7218
+ "New Upload Alt Text",
6674
7219
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
6675
7220
  "input",
6676
7221
  {
6677
7222
  onChange: (event) => setUploadAltText(event.target.value),
6678
- placeholder: "Describe the image",
7223
+ placeholder: "Used only when uploading a new image",
6679
7224
  style: sidebarInputStyle,
6680
7225
  type: "text",
6681
7226
  value: uploadAltText