@orion-studios/payload-studio 0.6.0-beta.8 → 0.7.0

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 (66) hide show
  1. package/README.md +6 -1
  2. package/bin/init.js +329 -0
  3. package/dist/admin/client.js +2106 -585
  4. package/dist/admin/client.mjs +2161 -653
  5. package/dist/admin/index.d.mts +2 -2
  6. package/dist/admin/index.d.ts +2 -2
  7. package/dist/admin/index.js +201 -37
  8. package/dist/admin/index.mjs +2 -2
  9. package/dist/admin-app/client.js +14 -5
  10. package/dist/admin-app/client.mjs +1 -1
  11. package/dist/admin-app/index.d.mts +2 -2
  12. package/dist/admin-app/index.d.ts +2 -2
  13. package/dist/admin-app/styles.css +361 -41
  14. package/dist/admin.css +98 -2
  15. package/dist/blocks/index.mjs +1 -2
  16. package/dist/builder-v2/client.d.mts +18 -0
  17. package/dist/builder-v2/client.d.ts +18 -0
  18. package/dist/builder-v2/client.js +4287 -0
  19. package/dist/builder-v2/client.mjs +4162 -0
  20. package/dist/builder-v2/index.d.mts +249 -0
  21. package/dist/builder-v2/index.d.ts +249 -0
  22. package/dist/builder-v2/index.js +836 -0
  23. package/dist/builder-v2/index.mjs +786 -0
  24. package/dist/builder-v2/styles.css +2705 -0
  25. package/dist/{chunk-PF3EBZXF.mjs → chunk-7ZMXZRBP.mjs} +39 -3
  26. package/dist/{chunk-XKUTZ7IU.mjs → chunk-DTHBPJXU.mjs} +246 -25
  27. package/dist/{chunk-JQAHXYAM.mjs → chunk-KMYMSR7W.mjs} +163 -3
  28. package/dist/{chunk-5FNTVRCR.mjs → chunk-MMJNHBOF.mjs} +204 -40
  29. package/dist/{chunk-KPIX7OSV.mjs → chunk-NF37A575.mjs} +14 -5
  30. package/dist/{chunk-OTHERBGX.mjs → chunk-OF6BATTO.mjs} +57 -112
  31. package/dist/chunk-VA545CHJ.mjs +202 -0
  32. package/dist/forms/index.d.mts +25 -0
  33. package/dist/forms/index.d.ts +25 -0
  34. package/dist/forms/index.js +237 -0
  35. package/dist/forms/index.mjs +25 -0
  36. package/dist/forms/server.d.mts +35 -0
  37. package/dist/forms/server.d.ts +35 -0
  38. package/dist/forms/server.js +442 -0
  39. package/dist/forms/server.mjs +228 -0
  40. package/dist/{index-Crx_MtPw.d.ts → index-0xXWuOuz.d.ts} +38 -6
  41. package/dist/{index-Cv-6qnrw.d.mts → index-BU82akSL.d.mts} +38 -6
  42. package/dist/{index-DyMmaRfI.d.mts → index-BbTcimgH.d.mts} +5 -2
  43. package/dist/{index-D8BNfUJb.d.mts → index-Cen-9wcc.d.mts} +17 -5
  44. package/dist/{index-52HdVLQq.d.ts → index-DAdN56fM.d.ts} +1 -1
  45. package/dist/{index-QPDAedIX.d.ts → index-DV-nW43e.d.ts} +5 -2
  46. package/dist/{index-DEQC3Dwj.d.mts → index-G_uTNffQ.d.mts} +1 -1
  47. package/dist/{index-DD_E2UfJ.d.ts → index-gxcMPpm-.d.ts} +17 -5
  48. package/dist/index.d.mts +5 -5
  49. package/dist/index.d.ts +5 -5
  50. package/dist/index.js +533 -172
  51. package/dist/index.mjs +10 -11
  52. package/dist/nextjs/index.d.mts +1 -1
  53. package/dist/nextjs/index.d.ts +1 -1
  54. package/dist/nextjs/index.js +95 -114
  55. package/dist/nextjs/index.mjs +2 -2
  56. package/dist/{sitePreviewTypes-BkHCWxNW.d.mts → sitePreviewTypes-BrJwGzJj.d.mts} +1 -1
  57. package/dist/{sitePreviewTypes-BkHCWxNW.d.ts → sitePreviewTypes-BrJwGzJj.d.ts} +1 -1
  58. package/dist/studio-pages/builder.css +66 -5
  59. package/dist/studio-pages/client.js +618 -73
  60. package/dist/studio-pages/client.mjs +641 -96
  61. package/dist/studio-pages/index.d.mts +1 -1
  62. package/dist/studio-pages/index.d.ts +1 -1
  63. package/dist/studio-pages/index.js +1893 -24
  64. package/dist/studio-pages/index.mjs +11 -3
  65. package/package.json +38 -3
  66. package/dist/chunk-OQSEJXC4.mjs +0 -166
@@ -20,9 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/studio-pages/index.ts
21
21
  var studio_pages_exports = {};
22
22
  __export(studio_pages_exports, {
23
+ DEFAULT_LEGACY_HERO_BACKGROUND_COLORS: () => DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
23
24
  createDefaultStudioDocument: () => createDefaultStudioDocument,
25
+ createStudioPageFields: () => createStudioPageFields,
24
26
  createStudioPageService: () => createStudioPageService,
27
+ createStudioPagesCollection: () => createStudioPagesCollection,
25
28
  defaultBuilderThemeTokens: () => defaultBuilderThemeTokens,
29
+ defaultRelationshipHydrationMap: () => defaultRelationshipHydrationMap,
26
30
  getStudioDocumentFromPage: () => getStudioDocumentFromPage,
27
31
  layoutToStudioDocument: () => layoutToStudioDocument,
28
32
  pageInspectorPanels: () => pageInspectorPanels,
@@ -54,6 +58,148 @@ var sectionStyleDefaults = {
54
58
  sectionPaddingY: "md",
55
59
  sectionWidth: "content"
56
60
  };
61
+ var hideFromCMS = (field) => ({
62
+ ...field,
63
+ admin: {
64
+ ...field.admin || {},
65
+ hidden: true
66
+ }
67
+ });
68
+ var sectionStyleFields = () => {
69
+ const fields = [
70
+ {
71
+ name: "sectionWidth",
72
+ type: "select",
73
+ defaultValue: sectionStyleDefaults.sectionWidth,
74
+ options: [
75
+ { label: "Content", value: "content" },
76
+ { label: "Wide", value: "wide" },
77
+ { label: "Full", value: "full" }
78
+ ]
79
+ },
80
+ {
81
+ name: "contentWidth",
82
+ type: "select",
83
+ defaultValue: sectionStyleDefaults.contentWidth,
84
+ options: [
85
+ { label: "Inherit", value: "inherit" },
86
+ { label: "Narrow", value: "narrow" },
87
+ { label: "Content", value: "content" },
88
+ { label: "Wide", value: "wide" },
89
+ { label: "Full", value: "full" }
90
+ ]
91
+ },
92
+ {
93
+ name: "sectionPaddingY",
94
+ type: "select",
95
+ defaultValue: sectionStyleDefaults.sectionPaddingY,
96
+ options: [
97
+ { label: "None", value: "none" },
98
+ { label: "Small", value: "sm" },
99
+ { label: "Medium", value: "md" },
100
+ { label: "Large", value: "lg" }
101
+ ]
102
+ },
103
+ {
104
+ name: "sectionPaddingX",
105
+ type: "select",
106
+ defaultValue: sectionStyleDefaults.sectionPaddingX,
107
+ options: [
108
+ { label: "Inherit", value: "inherit" },
109
+ { label: "None", value: "none" },
110
+ { label: "Small", value: "sm" },
111
+ { label: "Medium", value: "md" },
112
+ { label: "Large", value: "lg" }
113
+ ]
114
+ },
115
+ {
116
+ name: "sectionBackgroundMode",
117
+ type: "select",
118
+ defaultValue: sectionStyleDefaults.sectionBackgroundMode,
119
+ options: [
120
+ { label: "None", value: "none" },
121
+ { label: "Color", value: "color" },
122
+ { label: "Gradient", value: "gradient" }
123
+ ]
124
+ },
125
+ {
126
+ name: "sectionBackgroundColor",
127
+ type: "text",
128
+ defaultValue: sectionStyleDefaults.sectionBackgroundColor
129
+ },
130
+ {
131
+ name: "sectionGradientPreset",
132
+ type: "select",
133
+ defaultValue: sectionStyleDefaults.sectionGradientPreset,
134
+ options: [
135
+ { label: "None", value: "none" },
136
+ { label: "Brand", value: "brand" },
137
+ { label: "Forest", value: "forest" },
138
+ { label: "Moss", value: "moss" },
139
+ { label: "Cream", value: "cream" },
140
+ { label: "Slate", value: "slate" }
141
+ ]
142
+ },
143
+ {
144
+ name: "sectionGradientFrom",
145
+ type: "text",
146
+ defaultValue: sectionStyleDefaults.sectionGradientFrom
147
+ },
148
+ {
149
+ name: "sectionGradientTo",
150
+ type: "text",
151
+ defaultValue: sectionStyleDefaults.sectionGradientTo
152
+ },
153
+ {
154
+ name: "sectionGradientAngle",
155
+ type: "text",
156
+ defaultValue: sectionStyleDefaults.sectionGradientAngle
157
+ },
158
+ {
159
+ name: "contentBackgroundMode",
160
+ type: "select",
161
+ defaultValue: sectionStyleDefaults.contentBackgroundMode,
162
+ options: [
163
+ { label: "None", value: "none" },
164
+ { label: "Color", value: "color" },
165
+ { label: "Gradient", value: "gradient" }
166
+ ]
167
+ },
168
+ {
169
+ name: "contentBackgroundColor",
170
+ type: "text",
171
+ defaultValue: sectionStyleDefaults.contentBackgroundColor
172
+ },
173
+ {
174
+ name: "contentGradientPreset",
175
+ type: "select",
176
+ defaultValue: sectionStyleDefaults.contentGradientPreset,
177
+ options: [
178
+ { label: "None", value: "none" },
179
+ { label: "Cloud", value: "cloud" },
180
+ { label: "Sand", value: "sand" },
181
+ { label: "Mint", value: "mint" },
182
+ { label: "Night", value: "night" }
183
+ ]
184
+ },
185
+ {
186
+ name: "contentGradientFrom",
187
+ type: "text",
188
+ defaultValue: sectionStyleDefaults.contentGradientFrom
189
+ },
190
+ {
191
+ name: "contentGradientTo",
192
+ type: "text",
193
+ defaultValue: sectionStyleDefaults.contentGradientTo
194
+ },
195
+ {
196
+ name: "contentGradientAngle",
197
+ type: "text",
198
+ defaultValue: sectionStyleDefaults.contentGradientAngle
199
+ }
200
+ ];
201
+ return fields.map(hideFromCMS);
202
+ };
57
203
 
58
204
  // src/studio-pages/builder/settings-v2/types.ts
59
205
  var defaultBuilderBlockSettingsV2 = {
@@ -97,7 +243,9 @@ var defaultBuilderBlockSettingsV2 = {
97
243
  },
98
244
  typography: {
99
245
  bodyAlign: "left",
246
+ bodySizePt: null,
100
247
  headingAlign: "left",
248
+ headingSizePt: null,
101
249
  letterSpacingPreset: "normal",
102
250
  lineHeightPreset: "normal",
103
251
  maxTextWidth: "auto"
@@ -126,7 +274,9 @@ var defaultBuilderItemSettingsV2 = {
126
274
  },
127
275
  typography: {
128
276
  bodyAlign: "left",
277
+ bodySizePt: null,
129
278
  headingAlign: "left",
279
+ headingSizePt: null,
130
280
  letterSpacingPreset: "normal",
131
281
  lineHeightPreset: "normal",
132
282
  maxTextWidth: "auto"
@@ -156,6 +306,7 @@ var defaultBuilderThemeTokens = {
156
306
 
157
307
  // src/studio-pages/builder/adapters/settingsV2.ts
158
308
  var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
309
+ var isTextAlign = (value) => value === "left" || value === "center" || value === "right" || value === "justify";
159
310
  var parsePercent = (value) => {
160
311
  if (typeof value === "number" && Number.isFinite(value)) {
161
312
  return Math.max(0, Math.min(100, value));
@@ -180,6 +331,18 @@ var parsePixel = (value) => {
180
331
  }
181
332
  return null;
182
333
  };
334
+ var parsePoint = (value) => {
335
+ if (typeof value === "number" && Number.isFinite(value)) {
336
+ return Math.max(6, Math.min(120, Math.round(value)));
337
+ }
338
+ if (typeof value === "string" && value.trim().length > 0) {
339
+ const parsed = Number(value);
340
+ if (Number.isFinite(parsed)) {
341
+ return Math.max(6, Math.min(120, Math.round(parsed)));
342
+ }
343
+ }
344
+ return null;
345
+ };
183
346
  var mergeSettings = (defaults, input) => {
184
347
  if (!isRecord(input)) {
185
348
  return structuredClone(defaults);
@@ -198,6 +361,10 @@ var mergeSettings = (defaults, input) => {
198
361
  };
199
362
  var legacyBlockToV2Settings = (block) => {
200
363
  const current = structuredClone(defaultBuilderBlockSettingsV2);
364
+ if (block.blockType === "hero" && block.variant === "centered") {
365
+ current.typography.headingAlign = "center";
366
+ current.typography.bodyAlign = "center";
367
+ }
201
368
  current.layout.contentWidth = block.contentWidth === "narrow" || block.contentWidth === "content" || block.contentWidth === "wide" || block.contentWidth === "full" || block.contentWidth === "inherit" ? block.contentWidth : current.layout.contentWidth;
202
369
  current.layout.sectionPaddingX = block.sectionPaddingX === "none" || block.sectionPaddingX === "sm" || block.sectionPaddingX === "md" || block.sectionPaddingX === "lg" || block.sectionPaddingX === "inherit" ? block.sectionPaddingX : current.layout.sectionPaddingX;
203
370
  current.layout.sectionPaddingY = block.sectionPaddingY === "none" || block.sectionPaddingY === "sm" || block.sectionPaddingY === "lg" ? block.sectionPaddingY : current.layout.sectionPaddingY;
@@ -234,15 +401,28 @@ var legacyBlockToV2Settings = (block) => {
234
401
  current.media.positionX = parsePercent(block.imagePositionX ?? block.backgroundImagePositionX);
235
402
  current.media.positionY = parsePercent(block.imagePositionY ?? block.backgroundImagePositionY);
236
403
  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;
404
+ current.typography.headingAlign = isTextAlign(block.textHeadingAlign) ? block.textHeadingAlign : current.typography.headingAlign;
405
+ current.typography.bodyAlign = isTextAlign(block.textBodyAlign) ? block.textBodyAlign : current.typography.bodyAlign;
406
+ current.typography.headingSizePt = parsePoint(block.textHeadingSizePt);
407
+ current.typography.bodySizePt = parsePoint(block.textBodySizePt);
239
408
  current.typography.maxTextWidth = block.textMaxWidth === "auto" || block.textMaxWidth === "sm" || block.textMaxWidth === "md" || block.textMaxWidth === "lg" || block.textMaxWidth === "full" ? block.textMaxWidth : current.typography.maxTextWidth;
240
409
  current.typography.lineHeightPreset = block.textLineHeightPreset === "tight" || block.textLineHeightPreset === "normal" || block.textLineHeightPreset === "relaxed" ? block.textLineHeightPreset : current.typography.lineHeightPreset;
241
410
  current.typography.letterSpacingPreset = block.textLetterSpacingPreset === "tight" || block.textLetterSpacingPreset === "normal" || block.textLetterSpacingPreset === "relaxed" ? block.textLetterSpacingPreset : current.typography.letterSpacingPreset;
242
411
  current.advanced.editCopyInPanel = Boolean(block.editCopyInPanel ?? current.advanced.editCopyInPanel);
243
412
  current.advanced.customClassName = typeof block.customClassName === "string" ? block.customClassName : current.advanced.customClassName;
244
413
  current.advanced.hideOnMobile = Boolean(block.hideOnMobile ?? current.advanced.hideOnMobile);
245
- return mergeSettings(current, block.settings);
414
+ const settings = mergeSettings(current, block.settings);
415
+ if (block.blockType === "hero") {
416
+ const top = settings.layout.paddingTopPt;
417
+ const bottom = settings.layout.paddingBottomPt;
418
+ const hasLegacyLinkedHeroPadding = settings.layout.linkVerticalPadding !== false && (top === null || typeof top === "undefined" || top === 30) && (bottom === null || typeof bottom === "undefined" || bottom === 30 || bottom === 20);
419
+ if (hasLegacyLinkedHeroPadding) {
420
+ settings.layout.linkVerticalPadding = false;
421
+ settings.layout.paddingTopPt = 30;
422
+ settings.layout.paddingBottomPt = 20;
423
+ }
424
+ }
425
+ return settings;
246
426
  };
247
427
  var v2SettingsToLegacyBlock = (blockWithSettings) => {
248
428
  const settings = legacyBlockToV2Settings(blockWithSettings);
@@ -268,6 +448,8 @@ var v2SettingsToLegacyBlock = (blockWithSettings) => {
268
448
  next.contentGradientAngle = settings.appearance.contentGradientAngle;
269
449
  next.textHeadingAlign = settings.typography.headingAlign;
270
450
  next.textBodyAlign = settings.typography.bodyAlign;
451
+ next.textHeadingSizePt = settings.typography.headingSizePt;
452
+ next.textBodySizePt = settings.typography.bodySizePt;
271
453
  next.textMaxWidth = settings.typography.maxTextWidth;
272
454
  next.textLineHeightPreset = settings.typography.lineHeightPreset;
273
455
  next.textLetterSpacingPreset = settings.typography.letterSpacingPreset;
@@ -364,8 +546,52 @@ var alignOptions = [
364
546
  { label: "Right", value: "right" },
365
547
  { label: "Justify", value: "justify" }
366
548
  ];
367
- var layoutFieldSet = [];
549
+ var layoutFieldSet = [
550
+ {
551
+ group: "layout",
552
+ key: "settings.layout.linkVerticalPadding",
553
+ label: "Link Top/Bottom Padding",
554
+ tags: ["spacing", "padding", "vertical"],
555
+ type: "checkbox"
556
+ },
557
+ {
558
+ group: "layout",
559
+ key: "settings.layout.paddingTopPt",
560
+ label: "Top Padding (pt)",
561
+ max: 240,
562
+ min: 0,
563
+ tags: ["spacing", "padding", "top"],
564
+ type: "number"
565
+ },
566
+ {
567
+ group: "layout",
568
+ key: "settings.layout.paddingBottomPt",
569
+ label: "Bottom Padding (pt)",
570
+ max: 240,
571
+ min: 0,
572
+ tags: ["spacing", "padding", "bottom"],
573
+ type: "number"
574
+ }
575
+ ];
368
576
  var typographyFieldSet = [
577
+ {
578
+ group: "typography",
579
+ key: "settings.typography.headingSizePt",
580
+ label: "Heading Text Size (pt)",
581
+ max: 120,
582
+ min: 6,
583
+ tags: ["text", "size", "heading", "font"],
584
+ type: "number"
585
+ },
586
+ {
587
+ group: "typography",
588
+ key: "settings.typography.bodySizePt",
589
+ label: "Body Text Size (pt)",
590
+ max: 72,
591
+ min: 6,
592
+ tags: ["text", "size", "body", "paragraph", "font"],
593
+ type: "number"
594
+ },
369
595
  {
370
596
  group: "typography",
371
597
  key: "settings.typography.headingAlign",
@@ -582,6 +808,13 @@ var inspectorDefinitionByBlockType = {
582
808
  { group: "basics", inlineEditable: true, key: "kicker", label: "Kicker", type: "text" },
583
809
  { group: "basics", inlineEditable: true, key: "headline", label: "Headline", type: "text" },
584
810
  { group: "basics", inlineEditable: true, key: "subheadline", label: "Subheadline", type: "textarea" },
811
+ {
812
+ group: "basics",
813
+ key: "settings.marquee.itemsText",
814
+ label: "Scrolling Bar Options",
815
+ tags: ["marquee", "scrolling", "bar", "categories"],
816
+ type: "textarea"
817
+ },
585
818
  {
586
819
  group: "basics",
587
820
  key: "variant",
@@ -869,6 +1102,13 @@ function compileStudioDocument(document, modules) {
869
1102
  }
870
1103
 
871
1104
  // src/studio-pages/pageService.ts
1105
+ var defaultRelationshipHydrationMap = {
1106
+ beforeAfter: { arrays: { items: ["media", "beforeMedia", "afterMedia"] } },
1107
+ featureGrid: { arrays: { items: ["media", "beforeMedia", "afterMedia"] } },
1108
+ hero: { fields: ["media"] },
1109
+ logoWall: { arrays: { items: ["media", "beforeMedia", "afterMedia"] } },
1110
+ media: { fields: ["image"] }
1111
+ };
872
1112
  var isRecord4 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
873
1113
  var toRecordArray = (value) => Array.isArray(value) ? value.filter((item) => isRecord4(item)) : [];
874
1114
  var getRelationID = (value) => {
@@ -896,7 +1136,7 @@ var hydrateRelationship = (valueFromStudio, valueFromLayout) => {
896
1136
  }
897
1137
  return valueFromLayout;
898
1138
  };
899
- var hydrateDocumentWithLayoutRelations = (document, layout) => {
1139
+ var hydrateDocumentWithLayoutRelations = (document, layout, hydrationMap = defaultRelationshipHydrationMap) => {
900
1140
  const nextNodes = document.nodes.map((node, index) => {
901
1141
  const layoutBlock = layout[index];
902
1142
  if (!isRecord4(layoutBlock)) {
@@ -905,17 +1145,18 @@ var hydrateDocumentWithLayoutRelations = (document, layout) => {
905
1145
  if (typeof layoutBlock.blockType !== "string" || layoutBlock.blockType !== node.type) {
906
1146
  return node;
907
1147
  }
908
- const nextData = { ...node.data };
909
- if (node.type === "hero") {
910
- nextData.media = hydrateRelationship(nextData.media, layoutBlock.media);
1148
+ const rule = hydrationMap[node.type];
1149
+ if (!rule) {
1150
+ return node;
911
1151
  }
912
- if (node.type === "media") {
913
- nextData.image = hydrateRelationship(nextData.image, layoutBlock.image);
1152
+ const nextData = { ...node.data };
1153
+ for (const fieldName of rule.fields || []) {
1154
+ nextData[fieldName] = hydrateRelationship(nextData[fieldName], layoutBlock[fieldName]);
914
1155
  }
915
- if (node.type === "featureGrid" || node.type === "logoWall" || node.type === "beforeAfter") {
916
- const studioItems = Array.isArray(nextData.items) ? nextData.items : [];
917
- const layoutItems = Array.isArray(layoutBlock.items) ? layoutBlock.items : [];
918
- nextData.items = studioItems.map((rawStudioItem, itemIndex) => {
1156
+ for (const [arrayField, itemFields] of Object.entries(rule.arrays || {})) {
1157
+ const studioItems = Array.isArray(nextData[arrayField]) ? nextData[arrayField] : [];
1158
+ const layoutItems = Array.isArray(layoutBlock[arrayField]) ? layoutBlock[arrayField] : [];
1159
+ nextData[arrayField] = studioItems.map((rawStudioItem, itemIndex) => {
919
1160
  if (!isRecord4(rawStudioItem)) {
920
1161
  return rawStudioItem;
921
1162
  }
@@ -924,9 +1165,9 @@ var hydrateDocumentWithLayoutRelations = (document, layout) => {
924
1165
  return rawStudioItem;
925
1166
  }
926
1167
  const nextItem = { ...rawStudioItem };
927
- nextItem.media = hydrateRelationship(nextItem.media, layoutItem.media);
928
- nextItem.beforeMedia = hydrateRelationship(nextItem.beforeMedia, layoutItem.beforeMedia);
929
- nextItem.afterMedia = hydrateRelationship(nextItem.afterMedia, layoutItem.afterMedia);
1168
+ for (const itemField of itemFields) {
1169
+ nextItem[itemField] = hydrateRelationship(nextItem[itemField], layoutItem[itemField]);
1170
+ }
930
1171
  return nextItem;
931
1172
  });
932
1173
  }
@@ -945,7 +1186,12 @@ var normalizeDocument = (document) => ({
945
1186
  schemaVersion: 1,
946
1187
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
947
1188
  });
948
- var normalizeLegacyHeroDefaults = (document) => {
1189
+ var DEFAULT_LEGACY_HERO_BACKGROUND_COLORS = ["#124a37"];
1190
+ var normalizeLegacyHeroDefaults = (document, legacyColors = DEFAULT_LEGACY_HERO_BACKGROUND_COLORS) => {
1191
+ if (legacyColors.length === 0) {
1192
+ return document;
1193
+ }
1194
+ const normalizedLegacyColors = legacyColors.map((color) => color.trim().toLowerCase());
949
1195
  const nodes = document.nodes.map((node) => {
950
1196
  if (node.type !== "hero" || !isRecord4(node.data)) {
951
1197
  return node;
@@ -955,7 +1201,7 @@ var normalizeLegacyHeroDefaults = (document) => {
955
1201
  const sectionBackgroundMode = typeof nextData.sectionBackgroundMode === "string" ? nextData.sectionBackgroundMode : "none";
956
1202
  const hasBackgroundImageURL = typeof nextData.backgroundImageURL === "string" && nextData.backgroundImageURL.trim().length > 0;
957
1203
  const mediaRelation = getRelationID(nextData.media);
958
- if (heroBackgroundColor === "#124a37" && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
1204
+ if (normalizedLegacyColors.includes(heroBackgroundColor) && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
959
1205
  nextData.backgroundColor = "";
960
1206
  }
961
1207
  return {
@@ -974,13 +1220,13 @@ var assertCanPublish = (issues) => {
974
1220
  throw new Error(`Cannot publish page: ${publishErrors[0].message}`);
975
1221
  }
976
1222
  };
977
- var getStudioDocumentFromPage = (doc) => {
1223
+ var getStudioDocumentFromPage = (doc, relationshipHydration = defaultRelationshipHydrationMap) => {
978
1224
  const title = typeof doc.title === "string" ? doc.title : void 0;
979
1225
  const layout = asLayoutArray(doc.layout);
980
1226
  try {
981
1227
  const studioDocument = assertStudioDocumentV1(doc.studioDocument);
982
1228
  if (layout.length > 0) {
983
- return hydrateDocumentWithLayoutRelations(studioDocument, layout);
1229
+ return hydrateDocumentWithLayoutRelations(studioDocument, layout, relationshipHydration);
984
1230
  }
985
1231
  return studioDocument;
986
1232
  } catch {
@@ -992,8 +1238,10 @@ var getStudioDocumentFromPage = (doc) => {
992
1238
  };
993
1239
  var createStudioPageService = ({
994
1240
  collectionSlug = "pages",
1241
+ legacyHeroBackgroundColors = DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
995
1242
  modules,
996
1243
  payload,
1244
+ relationshipHydration = defaultRelationshipHydrationMap,
997
1245
  user
998
1246
  }) => ({
999
1247
  getPageForStudio: async (pageID) => {
@@ -1005,7 +1253,7 @@ var createStudioPageService = ({
1005
1253
  overrideAccess: false,
1006
1254
  user
1007
1255
  });
1008
- const studioDocument = getStudioDocumentFromPage(page);
1256
+ const studioDocument = getStudioDocumentFromPage(page, relationshipHydration);
1009
1257
  return {
1010
1258
  id: String(page.id),
1011
1259
  slug: typeof page.slug === "string" ? page.slug : "",
@@ -1015,7 +1263,10 @@ var createStudioPageService = ({
1015
1263
  },
1016
1264
  validateStudioDocument: (document) => validateStudioDocument(document, modules),
1017
1265
  saveDraft: async (pageID, document, _metadata) => {
1018
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
1266
+ const normalizedDocument = normalizeLegacyHeroDefaults(
1267
+ normalizeDocument(document),
1268
+ legacyHeroBackgroundColors
1269
+ );
1019
1270
  const compileResult = compileStudioDocument(normalizedDocument, modules);
1020
1271
  await payload.update({
1021
1272
  collection: collectionSlug,
@@ -1036,7 +1287,10 @@ var createStudioPageService = ({
1036
1287
  };
1037
1288
  },
1038
1289
  publish: async (pageID, document, _metadata) => {
1039
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
1290
+ const normalizedDocument = normalizeLegacyHeroDefaults(
1291
+ normalizeDocument(document),
1292
+ legacyHeroBackgroundColors
1293
+ );
1040
1294
  const compileResult = compileStudioDocument(normalizedDocument, modules);
1041
1295
  assertCanPublish(compileResult.issues);
1042
1296
  await payload.update({
@@ -1065,6 +1319,1617 @@ var toEditorInitialDoc = (payloadPage) => ({
1065
1319
  title: payloadPage.title
1066
1320
  });
1067
1321
 
1322
+ // src/blocks/blocks/adminComponents.ts
1323
+ var builderBlockLabelComponent = {
1324
+ exportName: "BuilderBlockLabel",
1325
+ path: "@orion-studios/payload-studio/blocks"
1326
+ };
1327
+
1328
+ // src/blocks/blocks/BeforeAfter.ts
1329
+ var BeforeAfterBlock = {
1330
+ slug: "beforeAfter",
1331
+ imageURL: "/images/project-before-2.svg",
1332
+ imageAltText: "Before and after section preview",
1333
+ admin: {
1334
+ components: {
1335
+ Label: builderBlockLabelComponent
1336
+ }
1337
+ },
1338
+ labels: {
1339
+ singular: "Before / After",
1340
+ plural: "Before / After"
1341
+ },
1342
+ fields: [
1343
+ {
1344
+ name: "title",
1345
+ type: "text",
1346
+ required: true
1347
+ },
1348
+ {
1349
+ name: "subtitle",
1350
+ type: "textarea"
1351
+ },
1352
+ {
1353
+ name: "itemsPerRow",
1354
+ type: "number",
1355
+ defaultValue: 2,
1356
+ min: 1,
1357
+ max: 4,
1358
+ admin: {
1359
+ description: "How many project cards to show per row on desktop.",
1360
+ step: 1
1361
+ }
1362
+ },
1363
+ {
1364
+ name: "items",
1365
+ type: "array",
1366
+ minRows: 1,
1367
+ maxRows: 30,
1368
+ fields: [
1369
+ {
1370
+ name: "label",
1371
+ type: "text",
1372
+ required: true
1373
+ },
1374
+ {
1375
+ name: "beforeMedia",
1376
+ type: "upload",
1377
+ relationTo: "media",
1378
+ required: false
1379
+ },
1380
+ {
1381
+ name: "beforeImageURL",
1382
+ type: "text",
1383
+ admin: {
1384
+ description: "Optional direct URL for the before image when using an external asset."
1385
+ }
1386
+ },
1387
+ {
1388
+ name: "afterMedia",
1389
+ type: "upload",
1390
+ relationTo: "media",
1391
+ required: false
1392
+ },
1393
+ {
1394
+ name: "afterImageURL",
1395
+ type: "text",
1396
+ admin: {
1397
+ description: "Optional direct URL for the after image when using an external asset."
1398
+ }
1399
+ },
1400
+ {
1401
+ name: "imageHeight",
1402
+ type: "number",
1403
+ defaultValue: 160,
1404
+ min: 60,
1405
+ max: 600,
1406
+ admin: {
1407
+ description: "Overrides the before/after image height (in pixels).",
1408
+ step: 10
1409
+ }
1410
+ },
1411
+ {
1412
+ name: "imageFit",
1413
+ type: "select",
1414
+ defaultValue: "cover",
1415
+ options: [
1416
+ { label: "Cover", value: "cover" },
1417
+ { label: "Contain", value: "contain" }
1418
+ ]
1419
+ },
1420
+ {
1421
+ name: "imageCornerStyle",
1422
+ type: "select",
1423
+ defaultValue: "rounded",
1424
+ options: [
1425
+ { label: "Rounded", value: "rounded" },
1426
+ { label: "Square", value: "square" }
1427
+ ]
1428
+ },
1429
+ {
1430
+ name: "imagePosition",
1431
+ type: "select",
1432
+ defaultValue: "center",
1433
+ options: [
1434
+ { label: "Center", value: "center" },
1435
+ { label: "Top", value: "top" },
1436
+ { label: "Bottom", value: "bottom" },
1437
+ { label: "Left", value: "left" },
1438
+ { label: "Right", value: "right" }
1439
+ ]
1440
+ },
1441
+ {
1442
+ name: "imagePositionX",
1443
+ type: "number",
1444
+ min: 0,
1445
+ max: 100,
1446
+ admin: {
1447
+ description: "Optional custom horizontal focus (0-100). Overrides Image Position when set.",
1448
+ step: 1
1449
+ }
1450
+ },
1451
+ {
1452
+ name: "imagePositionY",
1453
+ type: "number",
1454
+ min: 0,
1455
+ max: 100,
1456
+ admin: {
1457
+ description: "Optional custom vertical focus (0-100). Overrides Image Position when set.",
1458
+ step: 1
1459
+ }
1460
+ },
1461
+ {
1462
+ name: "description",
1463
+ type: "textarea"
1464
+ },
1465
+ {
1466
+ name: "settings",
1467
+ type: "json",
1468
+ admin: {
1469
+ hidden: true
1470
+ }
1471
+ }
1472
+ ]
1473
+ },
1474
+ {
1475
+ name: "settings",
1476
+ type: "json",
1477
+ admin: {
1478
+ description: "Internal builder settings schema v2.",
1479
+ hidden: true
1480
+ }
1481
+ },
1482
+ ...sectionStyleFields()
1483
+ ]
1484
+ };
1485
+
1486
+ // src/blocks/blocks/BookingEmbed.ts
1487
+ var BookingEmbedBlock = {
1488
+ slug: "bookingEmbed",
1489
+ imageURL: "/images/service-removal.svg",
1490
+ imageAltText: "Booking embed section preview",
1491
+ admin: {
1492
+ components: {
1493
+ Label: builderBlockLabelComponent
1494
+ }
1495
+ },
1496
+ labels: {
1497
+ singular: "Booking Embed",
1498
+ plural: "Booking Embeds"
1499
+ },
1500
+ fields: [
1501
+ {
1502
+ name: "title",
1503
+ type: "text"
1504
+ },
1505
+ {
1506
+ name: "description",
1507
+ type: "textarea"
1508
+ },
1509
+ {
1510
+ name: "buttonLabel",
1511
+ type: "text",
1512
+ defaultValue: "Book Consultation"
1513
+ },
1514
+ {
1515
+ name: "buttonHref",
1516
+ type: "text",
1517
+ defaultValue: "/contact"
1518
+ },
1519
+ {
1520
+ name: "settings",
1521
+ type: "json",
1522
+ admin: {
1523
+ description: "Internal builder settings schema v2.",
1524
+ hidden: true
1525
+ }
1526
+ },
1527
+ ...sectionStyleFields()
1528
+ ]
1529
+ };
1530
+
1531
+ // src/blocks/blocks/Cta.ts
1532
+ var CtaBlock = {
1533
+ slug: "cta",
1534
+ imageURL: "/images/logo-mark.svg",
1535
+ imageAltText: "Call to action section preview",
1536
+ admin: {
1537
+ components: {
1538
+ Label: builderBlockLabelComponent
1539
+ }
1540
+ },
1541
+ labels: {
1542
+ singular: "CTA",
1543
+ plural: "CTAs"
1544
+ },
1545
+ fields: [
1546
+ {
1547
+ name: "headline",
1548
+ type: "text",
1549
+ required: true
1550
+ },
1551
+ {
1552
+ name: "description",
1553
+ type: "textarea"
1554
+ },
1555
+ {
1556
+ name: "eyebrow",
1557
+ type: "text"
1558
+ },
1559
+ {
1560
+ name: "buttonLabel",
1561
+ type: "text"
1562
+ },
1563
+ {
1564
+ name: "buttonHref",
1565
+ type: "text",
1566
+ admin: {
1567
+ description: "Use internal links like /contact."
1568
+ }
1569
+ },
1570
+ {
1571
+ name: "style",
1572
+ type: "select",
1573
+ defaultValue: "light",
1574
+ options: [
1575
+ {
1576
+ label: "Light",
1577
+ value: "light"
1578
+ },
1579
+ {
1580
+ label: "Dark",
1581
+ value: "dark"
1582
+ }
1583
+ ]
1584
+ },
1585
+ {
1586
+ name: "backgroundColor",
1587
+ type: "text",
1588
+ admin: {
1589
+ description: "Optional background color override for the CTA strip (example: #334b63)."
1590
+ }
1591
+ },
1592
+ {
1593
+ name: "media",
1594
+ type: "upload",
1595
+ relationTo: "media",
1596
+ required: false
1597
+ },
1598
+ {
1599
+ name: "imageURL",
1600
+ type: "text",
1601
+ admin: {
1602
+ description: "Optional direct image URL when this CTA should use an external image."
1603
+ }
1604
+ },
1605
+ {
1606
+ name: "bullets",
1607
+ type: "array",
1608
+ fields: [
1609
+ {
1610
+ name: "label",
1611
+ type: "text",
1612
+ required: true
1613
+ }
1614
+ ]
1615
+ },
1616
+ {
1617
+ name: "settings",
1618
+ type: "json",
1619
+ admin: {
1620
+ description: "Internal builder settings schema v2.",
1621
+ hidden: true
1622
+ }
1623
+ },
1624
+ ...sectionStyleFields()
1625
+ ]
1626
+ };
1627
+
1628
+ // src/blocks/blocks/Faq.ts
1629
+ var FaqBlock = {
1630
+ slug: "faq",
1631
+ imageURL: "/images/project-before-2.svg",
1632
+ imageAltText: "FAQ section preview",
1633
+ admin: {
1634
+ components: {
1635
+ Label: builderBlockLabelComponent
1636
+ }
1637
+ },
1638
+ labels: {
1639
+ singular: "FAQ",
1640
+ plural: "FAQs"
1641
+ },
1642
+ fields: [
1643
+ {
1644
+ name: "eyebrow",
1645
+ type: "text"
1646
+ },
1647
+ {
1648
+ name: "title",
1649
+ type: "text",
1650
+ required: true
1651
+ },
1652
+ {
1653
+ name: "items",
1654
+ type: "array",
1655
+ minRows: 1,
1656
+ maxRows: 12,
1657
+ fields: [
1658
+ {
1659
+ name: "question",
1660
+ type: "text",
1661
+ required: true
1662
+ },
1663
+ {
1664
+ name: "answer",
1665
+ type: "textarea",
1666
+ required: true
1667
+ },
1668
+ {
1669
+ name: "settings",
1670
+ type: "json",
1671
+ admin: {
1672
+ hidden: true
1673
+ }
1674
+ }
1675
+ ]
1676
+ },
1677
+ {
1678
+ name: "settings",
1679
+ type: "json",
1680
+ admin: {
1681
+ description: "Internal builder settings schema v2.",
1682
+ hidden: true
1683
+ }
1684
+ },
1685
+ ...sectionStyleFields()
1686
+ ]
1687
+ };
1688
+
1689
+ // src/blocks/blocks/FeatureGrid.ts
1690
+ var FeatureGridBlock = {
1691
+ slug: "featureGrid",
1692
+ imageURL: "/images/service-trimming.svg",
1693
+ imageAltText: "Feature grid section preview",
1694
+ admin: {
1695
+ components: {
1696
+ Label: builderBlockLabelComponent
1697
+ }
1698
+ },
1699
+ labels: {
1700
+ singular: "Feature Grid",
1701
+ plural: "Feature Grids"
1702
+ },
1703
+ fields: [
1704
+ {
1705
+ name: "eyebrow",
1706
+ type: "text"
1707
+ },
1708
+ {
1709
+ name: "title",
1710
+ type: "text"
1711
+ },
1712
+ {
1713
+ name: "subtitle",
1714
+ type: "textarea"
1715
+ },
1716
+ {
1717
+ name: "itemsPerRow",
1718
+ type: "number",
1719
+ defaultValue: 3,
1720
+ min: 1,
1721
+ max: 6,
1722
+ admin: {
1723
+ description: "How many items to show per row on desktop.",
1724
+ step: 1
1725
+ }
1726
+ },
1727
+ {
1728
+ name: "items",
1729
+ type: "array",
1730
+ minRows: 1,
1731
+ maxRows: 6,
1732
+ fields: [
1733
+ {
1734
+ name: "title",
1735
+ type: "text",
1736
+ required: true
1737
+ },
1738
+ {
1739
+ name: "description",
1740
+ type: "textarea"
1741
+ },
1742
+ {
1743
+ name: "tone",
1744
+ type: "select",
1745
+ defaultValue: "warm",
1746
+ options: [
1747
+ { label: "Warm", value: "warm" },
1748
+ { label: "Cool", value: "cool" },
1749
+ { label: "Neutral", value: "neutral" }
1750
+ ]
1751
+ },
1752
+ {
1753
+ name: "iconType",
1754
+ type: "select",
1755
+ defaultValue: "badge",
1756
+ options: [
1757
+ { label: "Badge Text", value: "badge" },
1758
+ { label: "Icon", value: "lucide" }
1759
+ ],
1760
+ admin: {
1761
+ description: "Choose whether this item uses a short badge label or an icon."
1762
+ }
1763
+ },
1764
+ {
1765
+ name: "icon",
1766
+ type: "text",
1767
+ admin: {
1768
+ description: 'Optional short icon label (e.g. "01", "Leaf", "Star").',
1769
+ condition: (_, siblingData) => siblingData?.iconType !== "lucide"
1770
+ }
1771
+ },
1772
+ {
1773
+ name: "iconLucide",
1774
+ type: "select",
1775
+ options: [
1776
+ { label: "Shield Check", value: "ShieldCheck" },
1777
+ { label: "Clock", value: "Clock" },
1778
+ { label: "Leaf", value: "Leaf" },
1779
+ { label: "Dollar Sign", value: "DollarSign" },
1780
+ { label: "Hard Hat", value: "HardHat" },
1781
+ { label: "Sparkles", value: "Sparkles" },
1782
+ { label: "Tree Pine", value: "TreePine" },
1783
+ { label: "Badge Check", value: "BadgeCheck" },
1784
+ { label: "Calendar Clock", value: "CalendarClock" }
1785
+ ],
1786
+ admin: {
1787
+ description: "Select an icon for this item.",
1788
+ condition: (_, siblingData) => siblingData?.iconType === "lucide"
1789
+ }
1790
+ },
1791
+ {
1792
+ name: "media",
1793
+ type: "upload",
1794
+ relationTo: "media",
1795
+ required: false
1796
+ },
1797
+ {
1798
+ name: "imageURL",
1799
+ type: "text",
1800
+ admin: {
1801
+ description: "Optional direct image URL when this item should use an external image."
1802
+ }
1803
+ },
1804
+ {
1805
+ name: "embedURL",
1806
+ type: "text",
1807
+ admin: {
1808
+ description: "Optional iframe/embed URL for items like maps or other embedded content."
1809
+ }
1810
+ },
1811
+ {
1812
+ name: "buttonLabel",
1813
+ type: "text"
1814
+ },
1815
+ {
1816
+ name: "buttonHref",
1817
+ type: "text"
1818
+ },
1819
+ {
1820
+ name: "bullets",
1821
+ type: "array",
1822
+ fields: [
1823
+ {
1824
+ name: "label",
1825
+ type: "text",
1826
+ required: true
1827
+ }
1828
+ ]
1829
+ },
1830
+ {
1831
+ name: "imageHeight",
1832
+ type: "number",
1833
+ defaultValue: 160,
1834
+ min: 40,
1835
+ max: 600,
1836
+ admin: {
1837
+ description: "Overrides the image height in the builder/section (in pixels).",
1838
+ step: 10
1839
+ }
1840
+ },
1841
+ {
1842
+ name: "imageFit",
1843
+ type: "select",
1844
+ defaultValue: "cover",
1845
+ options: [
1846
+ { label: "Cover", value: "cover" },
1847
+ { label: "Contain", value: "contain" }
1848
+ ]
1849
+ },
1850
+ {
1851
+ name: "imageCornerStyle",
1852
+ type: "select",
1853
+ defaultValue: "rounded",
1854
+ options: [
1855
+ { label: "Rounded", value: "rounded" },
1856
+ { label: "Square", value: "square" }
1857
+ ]
1858
+ },
1859
+ {
1860
+ name: "imagePosition",
1861
+ type: "select",
1862
+ defaultValue: "center",
1863
+ options: [
1864
+ { label: "Center", value: "center" },
1865
+ { label: "Top", value: "top" },
1866
+ { label: "Bottom", value: "bottom" },
1867
+ { label: "Left", value: "left" },
1868
+ { label: "Right", value: "right" }
1869
+ ]
1870
+ },
1871
+ {
1872
+ name: "imagePositionX",
1873
+ type: "number",
1874
+ min: 0,
1875
+ max: 100,
1876
+ admin: {
1877
+ description: "Optional custom horizontal focus (0-100). Overrides Image Position when set.",
1878
+ step: 1
1879
+ }
1880
+ },
1881
+ {
1882
+ name: "imagePositionY",
1883
+ type: "number",
1884
+ min: 0,
1885
+ max: 100,
1886
+ admin: {
1887
+ description: "Optional custom vertical focus (0-100). Overrides Image Position when set.",
1888
+ step: 1
1889
+ }
1890
+ },
1891
+ {
1892
+ name: "settings",
1893
+ type: "json",
1894
+ admin: {
1895
+ hidden: true
1896
+ }
1897
+ }
1898
+ ]
1899
+ },
1900
+ {
1901
+ name: "variant",
1902
+ type: "select",
1903
+ defaultValue: "cards",
1904
+ options: [
1905
+ {
1906
+ label: "Cards",
1907
+ value: "cards"
1908
+ },
1909
+ {
1910
+ label: "Highlight",
1911
+ value: "highlight"
1912
+ },
1913
+ {
1914
+ label: "Split List",
1915
+ value: "splitList"
1916
+ },
1917
+ {
1918
+ label: "Panels",
1919
+ value: "panels"
1920
+ },
1921
+ {
1922
+ label: "Catalog",
1923
+ value: "catalog"
1924
+ },
1925
+ {
1926
+ label: "Contact Split",
1927
+ value: "contact"
1928
+ }
1929
+ ]
1930
+ },
1931
+ {
1932
+ name: "backgroundColor",
1933
+ type: "text",
1934
+ admin: {
1935
+ description: "Optional background color override when using the Highlight variant (example: #334b63)."
1936
+ }
1937
+ },
1938
+ {
1939
+ name: "settings",
1940
+ type: "json",
1941
+ admin: {
1942
+ description: "Internal builder settings schema v2.",
1943
+ hidden: true
1944
+ }
1945
+ },
1946
+ ...sectionStyleFields()
1947
+ ]
1948
+ };
1949
+
1950
+ // src/blocks/blocks/FormEmbed.ts
1951
+ var FormEmbedBlock = {
1952
+ slug: "formEmbed",
1953
+ imageURL: "/images/map-placeholder.svg",
1954
+ imageAltText: "Form embed section preview",
1955
+ admin: {
1956
+ components: {
1957
+ Label: builderBlockLabelComponent
1958
+ }
1959
+ },
1960
+ labels: {
1961
+ singular: "Form Embed",
1962
+ plural: "Form Embeds"
1963
+ },
1964
+ fields: [
1965
+ {
1966
+ name: "eyebrow",
1967
+ type: "text"
1968
+ },
1969
+ {
1970
+ name: "title",
1971
+ type: "text"
1972
+ },
1973
+ {
1974
+ name: "description",
1975
+ type: "textarea"
1976
+ },
1977
+ {
1978
+ name: "submitLabel",
1979
+ type: "text"
1980
+ },
1981
+ {
1982
+ name: "formType",
1983
+ type: "select",
1984
+ defaultValue: "quote",
1985
+ options: [
1986
+ {
1987
+ label: "Quote Form",
1988
+ value: "quote"
1989
+ }
1990
+ ]
1991
+ },
1992
+ {
1993
+ name: "settings",
1994
+ type: "json",
1995
+ admin: {
1996
+ description: "Internal builder settings schema v2.",
1997
+ hidden: true
1998
+ }
1999
+ },
2000
+ ...sectionStyleFields()
2001
+ ]
2002
+ };
2003
+
2004
+ // src/blocks/blocks/Hero.ts
2005
+ var HeroBlock = {
2006
+ slug: "hero",
2007
+ imageURL: "/images/hero-tree.svg",
2008
+ imageAltText: "Hero section preview",
2009
+ admin: {
2010
+ components: {
2011
+ Label: builderBlockLabelComponent
2012
+ }
2013
+ },
2014
+ labels: {
2015
+ singular: "Hero",
2016
+ plural: "Hero Sections"
2017
+ },
2018
+ fields: [
2019
+ {
2020
+ name: "kicker",
2021
+ type: "text",
2022
+ admin: {
2023
+ description: "Optional short label above the headline."
2024
+ }
2025
+ },
2026
+ {
2027
+ name: "headline",
2028
+ type: "text",
2029
+ required: true
2030
+ },
2031
+ {
2032
+ name: "subheadline",
2033
+ type: "textarea"
2034
+ },
2035
+ {
2036
+ name: "primaryLabel",
2037
+ type: "text"
2038
+ },
2039
+ {
2040
+ name: "primaryHref",
2041
+ type: "text",
2042
+ admin: {
2043
+ description: "Use internal links like /contact."
2044
+ }
2045
+ },
2046
+ {
2047
+ name: "secondaryLabel",
2048
+ type: "text"
2049
+ },
2050
+ {
2051
+ name: "secondaryHref",
2052
+ type: "text",
2053
+ admin: {
2054
+ description: "Use internal links like /services."
2055
+ }
2056
+ },
2057
+ {
2058
+ name: "media",
2059
+ type: "upload",
2060
+ relationTo: "media"
2061
+ },
2062
+ {
2063
+ name: "backgroundImageURL",
2064
+ type: "text",
2065
+ admin: {
2066
+ description: "Optional direct image URL when the hero should use an external image source."
2067
+ }
2068
+ },
2069
+ {
2070
+ name: "backgroundImageFit",
2071
+ type: "select",
2072
+ defaultValue: "cover",
2073
+ options: [
2074
+ { label: "Cover", value: "cover" },
2075
+ { label: "Cover (Square)", value: "cover-square" },
2076
+ { label: "Contain", value: "contain" },
2077
+ { label: "Contain (Square)", value: "contain-square" }
2078
+ ],
2079
+ admin: {
2080
+ description: "How the hero image should be sized within the section."
2081
+ }
2082
+ },
2083
+ {
2084
+ name: "backgroundImageCornerStyle",
2085
+ type: "select",
2086
+ defaultValue: "rounded",
2087
+ options: [
2088
+ { label: "Rounded", value: "rounded" },
2089
+ { label: "Square", value: "square" }
2090
+ ],
2091
+ admin: {
2092
+ description: "How the hero image corners should appear."
2093
+ }
2094
+ },
2095
+ {
2096
+ name: "backgroundImagePosition",
2097
+ type: "select",
2098
+ defaultValue: "center",
2099
+ options: [
2100
+ { label: "Center", value: "center" },
2101
+ { label: "Top", value: "top" },
2102
+ { label: "Bottom", value: "bottom" },
2103
+ { label: "Left", value: "left" },
2104
+ { label: "Right", value: "right" }
2105
+ ],
2106
+ admin: {
2107
+ description: "Where the hero image should anchor inside the section."
2108
+ }
2109
+ },
2110
+ {
2111
+ name: "backgroundColor",
2112
+ type: "text",
2113
+ admin: {
2114
+ description: "Optional background color override (example: #334b63)."
2115
+ }
2116
+ },
2117
+ {
2118
+ name: "backgroundOverlayMode",
2119
+ type: "select",
2120
+ defaultValue: "none",
2121
+ options: [
2122
+ { label: "None", value: "none" },
2123
+ { label: "Solid", value: "solid" },
2124
+ { label: "Gradient", value: "gradient" }
2125
+ ],
2126
+ admin: {
2127
+ description: "Optional overlay on top of the hero image (applies when an image is present)."
2128
+ }
2129
+ },
2130
+ {
2131
+ name: "backgroundOverlayOpacity",
2132
+ type: "number",
2133
+ defaultValue: 45,
2134
+ min: 0,
2135
+ max: 100,
2136
+ admin: {
2137
+ description: "Overlay opacity (0-100).",
2138
+ step: 1
2139
+ }
2140
+ },
2141
+ {
2142
+ name: "backgroundOverlayColor",
2143
+ type: "text",
2144
+ admin: {
2145
+ description: "Overlay solid color (example: #000000). Used when Overlay Mode is Solid."
2146
+ }
2147
+ },
2148
+ {
2149
+ name: "backgroundOverlayGradientFrom",
2150
+ type: "text",
2151
+ admin: {
2152
+ description: "Gradient overlay start color (example: #334b63). Used when Overlay Mode is Gradient."
2153
+ }
2154
+ },
2155
+ {
2156
+ name: "backgroundOverlayGradientTo",
2157
+ type: "text",
2158
+ admin: {
2159
+ description: "Gradient overlay end color (example: #496582). Used when Overlay Mode is Gradient."
2160
+ }
2161
+ },
2162
+ {
2163
+ name: "backgroundOverlayGradientAngle",
2164
+ type: "text",
2165
+ admin: {
2166
+ description: "Gradient overlay angle in degrees (0-360). Used when Overlay Mode is Gradient."
2167
+ }
2168
+ },
2169
+ {
2170
+ name: "backgroundOverlayGradientFromStrength",
2171
+ type: "number",
2172
+ defaultValue: 100,
2173
+ min: 0,
2174
+ max: 100,
2175
+ admin: {
2176
+ description: "Gradient start strength (0-100). Set to 0 for transparent.",
2177
+ step: 1
2178
+ }
2179
+ },
2180
+ {
2181
+ name: "backgroundOverlayGradientToStrength",
2182
+ type: "number",
2183
+ defaultValue: 100,
2184
+ min: 0,
2185
+ max: 100,
2186
+ admin: {
2187
+ description: "Gradient end strength (0-100). Set to 0 for transparent.",
2188
+ step: 1
2189
+ }
2190
+ },
2191
+ {
2192
+ name: "backgroundOverlayGradientStart",
2193
+ type: "number",
2194
+ defaultValue: 0,
2195
+ min: 0,
2196
+ max: 100,
2197
+ admin: {
2198
+ description: "Where the gradient starts (0-100).",
2199
+ step: 1
2200
+ }
2201
+ },
2202
+ {
2203
+ name: "backgroundOverlayGradientEnd",
2204
+ type: "number",
2205
+ defaultValue: 100,
2206
+ min: 0,
2207
+ max: 100,
2208
+ admin: {
2209
+ description: "Where the gradient ends (0-100).",
2210
+ step: 1
2211
+ }
2212
+ },
2213
+ {
2214
+ name: "backgroundOverlayGradientFeather",
2215
+ type: "number",
2216
+ defaultValue: 100,
2217
+ min: 0,
2218
+ max: 100,
2219
+ admin: {
2220
+ description: "How soft the transition is (0 = hard edge, 100 = smooth).",
2221
+ step: 1
2222
+ }
2223
+ },
2224
+ {
2225
+ name: "variant",
2226
+ type: "select",
2227
+ defaultValue: "default",
2228
+ options: [
2229
+ {
2230
+ label: "Default",
2231
+ value: "default"
2232
+ },
2233
+ {
2234
+ label: "Centered",
2235
+ value: "centered"
2236
+ }
2237
+ ]
2238
+ },
2239
+ {
2240
+ name: "heroHeight",
2241
+ type: "select",
2242
+ defaultValue: "sm",
2243
+ options: [
2244
+ {
2245
+ label: "Small",
2246
+ value: "sm"
2247
+ },
2248
+ {
2249
+ label: "Medium (Half Screen)",
2250
+ value: "md"
2251
+ },
2252
+ {
2253
+ label: "Full Screen",
2254
+ value: "full"
2255
+ }
2256
+ ],
2257
+ admin: {
2258
+ description: "Controls the vertical height of the hero section."
2259
+ }
2260
+ },
2261
+ {
2262
+ name: "settings",
2263
+ type: "json",
2264
+ admin: {
2265
+ description: "Internal builder settings schema v2.",
2266
+ hidden: true
2267
+ }
2268
+ },
2269
+ ...sectionStyleFields()
2270
+ ]
2271
+ };
2272
+
2273
+ // src/blocks/blocks/LogoWall.ts
2274
+ var LogoWallBlock = {
2275
+ slug: "logoWall",
2276
+ imageURL: "/images/logo-mark.svg",
2277
+ imageAltText: "Logo wall section preview",
2278
+ admin: {
2279
+ components: {
2280
+ Label: builderBlockLabelComponent
2281
+ }
2282
+ },
2283
+ labels: {
2284
+ singular: "Logo Wall",
2285
+ plural: "Logo Walls"
2286
+ },
2287
+ fields: [
2288
+ {
2289
+ name: "title",
2290
+ type: "text",
2291
+ required: true
2292
+ },
2293
+ {
2294
+ name: "subtitle",
2295
+ type: "textarea"
2296
+ },
2297
+ {
2298
+ name: "items",
2299
+ type: "array",
2300
+ minRows: 1,
2301
+ maxRows: 12,
2302
+ fields: [
2303
+ {
2304
+ name: "name",
2305
+ type: "text",
2306
+ required: true
2307
+ },
2308
+ {
2309
+ name: "media",
2310
+ type: "upload",
2311
+ relationTo: "media",
2312
+ required: false
2313
+ },
2314
+ {
2315
+ name: "imageURL",
2316
+ type: "text",
2317
+ admin: {
2318
+ description: "Optional direct image URL for this logo when using an external asset."
2319
+ }
2320
+ },
2321
+ {
2322
+ name: "imageHeight",
2323
+ type: "number",
2324
+ defaultValue: 64,
2325
+ min: 24,
2326
+ max: 200,
2327
+ admin: {
2328
+ description: "Overrides the logo image height (in pixels).",
2329
+ step: 4
2330
+ }
2331
+ },
2332
+ {
2333
+ name: "imageFit",
2334
+ type: "select",
2335
+ defaultValue: "contain",
2336
+ options: [
2337
+ { label: "Cover", value: "cover" },
2338
+ { label: "Contain", value: "contain" }
2339
+ ]
2340
+ },
2341
+ {
2342
+ name: "imageCornerStyle",
2343
+ type: "select",
2344
+ defaultValue: "rounded",
2345
+ options: [
2346
+ { label: "Rounded", value: "rounded" },
2347
+ { label: "Square", value: "square" }
2348
+ ]
2349
+ },
2350
+ {
2351
+ name: "imagePosition",
2352
+ type: "select",
2353
+ defaultValue: "center",
2354
+ options: [
2355
+ { label: "Center", value: "center" },
2356
+ { label: "Top", value: "top" },
2357
+ { label: "Bottom", value: "bottom" },
2358
+ { label: "Left", value: "left" },
2359
+ { label: "Right", value: "right" }
2360
+ ]
2361
+ },
2362
+ {
2363
+ name: "imagePositionX",
2364
+ type: "number",
2365
+ min: 0,
2366
+ max: 100,
2367
+ admin: {
2368
+ description: "Optional custom horizontal focus (0-100). Overrides Image Position when set.",
2369
+ step: 1
2370
+ }
2371
+ },
2372
+ {
2373
+ name: "imagePositionY",
2374
+ type: "number",
2375
+ min: 0,
2376
+ max: 100,
2377
+ admin: {
2378
+ description: "Optional custom vertical focus (0-100). Overrides Image Position when set.",
2379
+ step: 1
2380
+ }
2381
+ },
2382
+ {
2383
+ name: "href",
2384
+ type: "text"
2385
+ },
2386
+ {
2387
+ name: "settings",
2388
+ type: "json",
2389
+ admin: {
2390
+ hidden: true
2391
+ }
2392
+ }
2393
+ ]
2394
+ },
2395
+ {
2396
+ name: "settings",
2397
+ type: "json",
2398
+ admin: {
2399
+ description: "Internal builder settings schema v2.",
2400
+ hidden: true
2401
+ }
2402
+ },
2403
+ ...sectionStyleFields()
2404
+ ]
2405
+ };
2406
+
2407
+ // src/blocks/blocks/Media.ts
2408
+ var MediaBlock = {
2409
+ slug: "media",
2410
+ imageURL: "/images/project-after-1.svg",
2411
+ imageAltText: "Media section preview",
2412
+ admin: {
2413
+ components: {
2414
+ Label: builderBlockLabelComponent
2415
+ }
2416
+ },
2417
+ labels: {
2418
+ singular: "Media",
2419
+ plural: "Media Sections"
2420
+ },
2421
+ fields: [
2422
+ {
2423
+ name: "image",
2424
+ type: "upload",
2425
+ relationTo: "media",
2426
+ required: false
2427
+ },
2428
+ {
2429
+ name: "imageURL",
2430
+ type: "text",
2431
+ admin: {
2432
+ description: "Optional direct image URL when this section should use an external image."
2433
+ }
2434
+ },
2435
+ {
2436
+ name: "caption",
2437
+ type: "text"
2438
+ },
2439
+ {
2440
+ name: "size",
2441
+ type: "select",
2442
+ defaultValue: "default",
2443
+ options: [
2444
+ {
2445
+ label: "Default",
2446
+ value: "default"
2447
+ },
2448
+ {
2449
+ label: "Wide",
2450
+ value: "wide"
2451
+ }
2452
+ ]
2453
+ },
2454
+ {
2455
+ name: "imageFit",
2456
+ type: "select",
2457
+ defaultValue: "cover",
2458
+ options: [
2459
+ { label: "Cover", value: "cover" },
2460
+ { label: "Contain", value: "contain" }
2461
+ ]
2462
+ },
2463
+ {
2464
+ name: "imageCornerStyle",
2465
+ type: "select",
2466
+ defaultValue: "rounded",
2467
+ options: [
2468
+ { label: "Rounded", value: "rounded" },
2469
+ { label: "Square", value: "square" }
2470
+ ]
2471
+ },
2472
+ {
2473
+ name: "imagePosition",
2474
+ type: "select",
2475
+ defaultValue: "center",
2476
+ options: [
2477
+ { label: "Center", value: "center" },
2478
+ { label: "Top", value: "top" },
2479
+ { label: "Bottom", value: "bottom" },
2480
+ { label: "Left", value: "left" },
2481
+ { label: "Right", value: "right" }
2482
+ ]
2483
+ },
2484
+ {
2485
+ name: "settings",
2486
+ type: "json",
2487
+ admin: {
2488
+ description: "Internal builder settings schema v2.",
2489
+ hidden: true
2490
+ }
2491
+ },
2492
+ ...sectionStyleFields()
2493
+ ]
2494
+ };
2495
+
2496
+ // src/blocks/blocks/RichText.ts
2497
+ var RichTextBlock = {
2498
+ slug: "richText",
2499
+ imageURL: "/window.svg",
2500
+ imageAltText: "Rich text section preview",
2501
+ admin: {
2502
+ components: {
2503
+ Label: builderBlockLabelComponent
2504
+ }
2505
+ },
2506
+ labels: {
2507
+ singular: "Rich Text",
2508
+ plural: "Rich Text Sections"
2509
+ },
2510
+ fields: [
2511
+ {
2512
+ name: "variant",
2513
+ type: "select",
2514
+ defaultValue: "default",
2515
+ options: [
2516
+ {
2517
+ label: "Default",
2518
+ value: "default"
2519
+ },
2520
+ {
2521
+ label: "Quote Banner",
2522
+ value: "quoteBanner"
2523
+ }
2524
+ ]
2525
+ },
2526
+ {
2527
+ name: "title",
2528
+ type: "text"
2529
+ },
2530
+ {
2531
+ name: "content",
2532
+ type: "richText",
2533
+ required: true
2534
+ },
2535
+ {
2536
+ name: "statsItems",
2537
+ type: "array",
2538
+ fields: [
2539
+ {
2540
+ name: "value",
2541
+ type: "text",
2542
+ required: true
2543
+ },
2544
+ {
2545
+ name: "label",
2546
+ type: "text",
2547
+ required: true
2548
+ }
2549
+ ]
2550
+ },
2551
+ {
2552
+ name: "cards",
2553
+ type: "array",
2554
+ fields: [
2555
+ {
2556
+ name: "eyebrow",
2557
+ type: "text"
2558
+ },
2559
+ {
2560
+ name: "title",
2561
+ type: "text",
2562
+ required: true
2563
+ },
2564
+ {
2565
+ name: "description",
2566
+ type: "textarea"
2567
+ },
2568
+ {
2569
+ name: "media",
2570
+ type: "upload",
2571
+ relationTo: "media",
2572
+ required: false
2573
+ },
2574
+ {
2575
+ name: "imageURL",
2576
+ type: "text",
2577
+ admin: {
2578
+ description: "Optional direct image URL when this card should use an external image."
2579
+ }
2580
+ }
2581
+ ]
2582
+ },
2583
+ {
2584
+ name: "width",
2585
+ type: "select",
2586
+ defaultValue: "normal",
2587
+ options: [
2588
+ {
2589
+ label: "Normal",
2590
+ value: "normal"
2591
+ },
2592
+ {
2593
+ label: "Narrow",
2594
+ value: "narrow"
2595
+ }
2596
+ ]
2597
+ },
2598
+ {
2599
+ name: "settings",
2600
+ type: "json",
2601
+ admin: {
2602
+ description: "Internal builder settings schema v2.",
2603
+ hidden: true
2604
+ }
2605
+ },
2606
+ ...sectionStyleFields()
2607
+ ]
2608
+ };
2609
+
2610
+ // src/blocks/blocks/Stats.ts
2611
+ var StatsBlock = {
2612
+ slug: "stats",
2613
+ imageURL: "/images/service-removal.svg",
2614
+ imageAltText: "Stats section preview",
2615
+ admin: {
2616
+ components: {
2617
+ Label: builderBlockLabelComponent
2618
+ }
2619
+ },
2620
+ labels: {
2621
+ singular: "Stats",
2622
+ plural: "Stats"
2623
+ },
2624
+ fields: [
2625
+ {
2626
+ name: "title",
2627
+ type: "text",
2628
+ required: true
2629
+ },
2630
+ {
2631
+ name: "subtitle",
2632
+ type: "textarea"
2633
+ },
2634
+ {
2635
+ name: "items",
2636
+ type: "array",
2637
+ minRows: 1,
2638
+ maxRows: 6,
2639
+ fields: [
2640
+ {
2641
+ name: "value",
2642
+ type: "text",
2643
+ required: true
2644
+ },
2645
+ {
2646
+ name: "label",
2647
+ type: "text",
2648
+ required: true
2649
+ },
2650
+ {
2651
+ name: "description",
2652
+ type: "text"
2653
+ },
2654
+ {
2655
+ name: "settings",
2656
+ type: "json",
2657
+ admin: {
2658
+ hidden: true
2659
+ }
2660
+ }
2661
+ ]
2662
+ },
2663
+ {
2664
+ name: "settings",
2665
+ type: "json",
2666
+ admin: {
2667
+ description: "Internal builder settings schema v2.",
2668
+ hidden: true
2669
+ }
2670
+ },
2671
+ ...sectionStyleFields()
2672
+ ]
2673
+ };
2674
+
2675
+ // src/blocks/blocks/Testimonials.ts
2676
+ var TestimonialsBlock = {
2677
+ slug: "testimonials",
2678
+ imageURL: "/images/project-after-2.svg",
2679
+ imageAltText: "Testimonials section preview",
2680
+ admin: {
2681
+ components: {
2682
+ Label: builderBlockLabelComponent
2683
+ }
2684
+ },
2685
+ labels: {
2686
+ singular: "Testimonials",
2687
+ plural: "Testimonials"
2688
+ },
2689
+ fields: [
2690
+ {
2691
+ name: "title",
2692
+ type: "text",
2693
+ required: true
2694
+ },
2695
+ {
2696
+ name: "visibleCount",
2697
+ type: "number",
2698
+ defaultValue: 3,
2699
+ min: 1,
2700
+ max: 6,
2701
+ admin: {
2702
+ description: "How many testimonials to show at once.",
2703
+ step: 1
2704
+ }
2705
+ },
2706
+ {
2707
+ name: "autoRotate",
2708
+ type: "checkbox",
2709
+ defaultValue: true,
2710
+ admin: {
2711
+ description: "Automatically rotates through all testimonials."
2712
+ }
2713
+ },
2714
+ {
2715
+ name: "rotateIntervalSeconds",
2716
+ type: "number",
2717
+ defaultValue: 7,
2718
+ min: 2,
2719
+ max: 30,
2720
+ admin: {
2721
+ description: "How often to rotate (in seconds).",
2722
+ step: 1
2723
+ }
2724
+ },
2725
+ {
2726
+ name: "items",
2727
+ type: "array",
2728
+ minRows: 1,
2729
+ maxRows: 30,
2730
+ fields: [
2731
+ {
2732
+ name: "quote",
2733
+ type: "textarea",
2734
+ required: true
2735
+ },
2736
+ {
2737
+ name: "name",
2738
+ type: "text",
2739
+ required: true
2740
+ },
2741
+ {
2742
+ name: "location",
2743
+ type: "text"
2744
+ },
2745
+ {
2746
+ name: "rating",
2747
+ type: "number",
2748
+ defaultValue: 5,
2749
+ min: 1,
2750
+ max: 5,
2751
+ admin: {
2752
+ description: "Star rating (1-5).",
2753
+ step: 1
2754
+ }
2755
+ },
2756
+ {
2757
+ name: "settings",
2758
+ type: "json",
2759
+ admin: {
2760
+ hidden: true
2761
+ }
2762
+ }
2763
+ ]
2764
+ },
2765
+ {
2766
+ name: "settings",
2767
+ type: "json",
2768
+ admin: {
2769
+ description: "Internal builder settings schema v2.",
2770
+ hidden: true
2771
+ }
2772
+ },
2773
+ ...sectionStyleFields()
2774
+ ]
2775
+ };
2776
+
2777
+ // src/blocks/index.ts
2778
+ var defaultPageLayoutBlocks = [
2779
+ HeroBlock,
2780
+ RichTextBlock,
2781
+ FeatureGridBlock,
2782
+ StatsBlock,
2783
+ LogoWallBlock,
2784
+ BeforeAfterBlock,
2785
+ MediaBlock,
2786
+ TestimonialsBlock,
2787
+ FaqBlock,
2788
+ FormEmbedBlock,
2789
+ BookingEmbedBlock,
2790
+ CtaBlock
2791
+ ];
2792
+
2793
+ // src/studio-pages/pageFields.ts
2794
+ var formatSlugValue = (value) => {
2795
+ if (typeof value !== "string") return void 0;
2796
+ return value.trim().toLowerCase().replace(/[^a-z0-9/]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2797
+ };
2798
+ function createStudioPageFields(options = {}) {
2799
+ const {
2800
+ blocks = defaultPageLayoutBlocks,
2801
+ mediaCollectionSlug = "media",
2802
+ additionalFields = []
2803
+ } = options;
2804
+ return [
2805
+ {
2806
+ name: "title",
2807
+ type: "text",
2808
+ required: true
2809
+ },
2810
+ {
2811
+ name: "slug",
2812
+ type: "text",
2813
+ admin: {
2814
+ description: "URL identifier for this page. `home` is served at `/`.",
2815
+ position: "sidebar"
2816
+ },
2817
+ hooks: {
2818
+ beforeValidate: [({ value }) => formatSlugValue(value) ?? value]
2819
+ },
2820
+ index: true,
2821
+ required: true,
2822
+ unique: true
2823
+ },
2824
+ {
2825
+ name: "path",
2826
+ type: "text",
2827
+ admin: {
2828
+ description: "Full public path. Derived from the slug when left empty.",
2829
+ position: "sidebar"
2830
+ },
2831
+ hooks: {
2832
+ beforeValidate: [
2833
+ ({ value, data }) => {
2834
+ if (typeof value === "string" && value.trim().length > 0) {
2835
+ return value.trim();
2836
+ }
2837
+ const slug = formatSlugValue(data?.slug);
2838
+ if (!slug) return value;
2839
+ return slug === "home" ? "/" : `/${slug}`;
2840
+ }
2841
+ ]
2842
+ },
2843
+ index: true,
2844
+ unique: true
2845
+ },
2846
+ {
2847
+ name: "studioDocument",
2848
+ type: "json",
2849
+ admin: {
2850
+ description: "Canonical Studio editor state. Managed by the page builder.",
2851
+ hidden: true
2852
+ }
2853
+ },
2854
+ {
2855
+ name: "studioValidationIssues",
2856
+ type: "json",
2857
+ admin: {
2858
+ description: "Validation issues from the last Studio compile. Managed by the page builder.",
2859
+ hidden: true
2860
+ }
2861
+ },
2862
+ {
2863
+ name: "layout",
2864
+ type: "blocks",
2865
+ admin: {
2866
+ description: "Compiled output only \u2014 edit pages through the Studio builder."
2867
+ },
2868
+ blocks
2869
+ },
2870
+ {
2871
+ name: "seo",
2872
+ type: "group",
2873
+ fields: [
2874
+ { name: "metaTitle", type: "text" },
2875
+ { name: "metaDescription", type: "textarea" },
2876
+ { name: "canonicalUrl", type: "text" },
2877
+ {
2878
+ name: "ogImage",
2879
+ type: "upload",
2880
+ relationTo: mediaCollectionSlug
2881
+ }
2882
+ ]
2883
+ },
2884
+ {
2885
+ name: "noIndex",
2886
+ type: "checkbox",
2887
+ admin: { position: "sidebar" },
2888
+ defaultValue: false
2889
+ },
2890
+ {
2891
+ name: "noFollow",
2892
+ type: "checkbox",
2893
+ admin: { position: "sidebar" },
2894
+ defaultValue: false
2895
+ },
2896
+ ...additionalFields
2897
+ ];
2898
+ }
2899
+ function createStudioPagesCollection(options = {}) {
2900
+ const { slug = "pages", access, overrides, ...fieldOptions } = options;
2901
+ const base = {
2902
+ slug,
2903
+ access: access || {
2904
+ create: ({ req }) => Boolean(req.user),
2905
+ delete: ({ req }) => Boolean(req.user),
2906
+ read: ({ req }) => {
2907
+ if (req.user) return true;
2908
+ return { _status: { equals: "published" } };
2909
+ },
2910
+ update: ({ req }) => Boolean(req.user)
2911
+ },
2912
+ admin: {
2913
+ defaultColumns: ["title", "slug", "_status", "updatedAt"],
2914
+ useAsTitle: "title"
2915
+ },
2916
+ fields: createStudioPageFields(fieldOptions),
2917
+ versions: {
2918
+ drafts: {
2919
+ autosave: false
2920
+ },
2921
+ maxPerDoc: 50
2922
+ }
2923
+ };
2924
+ if (!overrides) return base;
2925
+ return {
2926
+ ...base,
2927
+ ...overrides,
2928
+ admin: { ...base.admin, ...overrides.admin },
2929
+ fields: overrides.fields || base.fields
2930
+ };
2931
+ }
2932
+
1068
2933
  // src/studio-pages/migrations.ts
1069
2934
  var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
1070
2935
  var assertPageStudioDocumentV1 = (value) => {
@@ -1419,9 +3284,13 @@ var pageStudioModuleManifest = {
1419
3284
  };
1420
3285
  // Annotate the CommonJS export names for ESM import in node:
1421
3286
  0 && (module.exports = {
3287
+ DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
1422
3288
  createDefaultStudioDocument,
3289
+ createStudioPageFields,
1423
3290
  createStudioPageService,
3291
+ createStudioPagesCollection,
1424
3292
  defaultBuilderThemeTokens,
3293
+ defaultRelationshipHydrationMap,
1425
3294
  getStudioDocumentFromPage,
1426
3295
  layoutToStudioDocument,
1427
3296
  pageInspectorPanels,