@orion-studios/payload-studio 0.6.0-beta.185 → 0.6.0-beta.186

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 (40) hide show
  1. package/README.md +6 -1
  2. package/bin/init.js +329 -0
  3. package/dist/admin/index.d.mts +1 -1
  4. package/dist/admin/index.d.ts +1 -1
  5. package/dist/admin/index.js +61 -20
  6. package/dist/admin/index.mjs +1 -1
  7. package/dist/blocks/index.mjs +1 -2
  8. package/dist/{chunk-NGLIA2OE.mjs → chunk-DTHBPJXU.mjs} +193 -23
  9. package/dist/{chunk-JQAHXYAM.mjs → chunk-KMYMSR7W.mjs} +163 -3
  10. package/dist/{chunk-KHK6RTGC.mjs → chunk-MMJNHBOF.mjs} +61 -20
  11. package/dist/{chunk-ZADL33R6.mjs → chunk-OF6BATTO.mjs} +56 -111
  12. package/dist/chunk-VA545CHJ.mjs +202 -0
  13. package/dist/forms/index.d.mts +25 -0
  14. package/dist/forms/index.d.ts +25 -0
  15. package/dist/forms/index.js +237 -0
  16. package/dist/forms/index.mjs +25 -0
  17. package/dist/forms/server.d.mts +35 -0
  18. package/dist/forms/server.d.ts +35 -0
  19. package/dist/forms/server.js +442 -0
  20. package/dist/forms/server.mjs +228 -0
  21. package/dist/{index-Dv-Alx4h.d.ts → index-0xXWuOuz.d.ts} +35 -5
  22. package/dist/{index-D5zrOdyv.d.mts → index-BU82akSL.d.mts} +35 -5
  23. package/dist/{index-DLfPOqYA.d.mts → index-BbTcimgH.d.mts} +1 -0
  24. package/dist/{index-D8BNfUJb.d.mts → index-Cen-9wcc.d.mts} +17 -5
  25. package/dist/{index-BV0vEGl6.d.ts → index-DV-nW43e.d.ts} +1 -0
  26. package/dist/{index-DD_E2UfJ.d.ts → index-gxcMPpm-.d.ts} +17 -5
  27. package/dist/index.d.mts +3 -3
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +302 -151
  30. package/dist/index.mjs +11 -12
  31. package/dist/nextjs/index.d.mts +1 -1
  32. package/dist/nextjs/index.d.ts +1 -1
  33. package/dist/nextjs/index.js +56 -111
  34. package/dist/nextjs/index.mjs +1 -1
  35. package/dist/studio-pages/index.d.mts +1 -1
  36. package/dist/studio-pages/index.d.ts +1 -1
  37. package/dist/studio-pages/index.js +1802 -20
  38. package/dist/studio-pages/index.mjs +10 -2
  39. package/package.json +18 -2
  40. 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 = {
@@ -956,6 +1102,13 @@ function compileStudioDocument(document, modules) {
956
1102
  }
957
1103
 
958
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
+ };
959
1112
  var isRecord4 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
960
1113
  var toRecordArray = (value) => Array.isArray(value) ? value.filter((item) => isRecord4(item)) : [];
961
1114
  var getRelationID = (value) => {
@@ -983,7 +1136,7 @@ var hydrateRelationship = (valueFromStudio, valueFromLayout) => {
983
1136
  }
984
1137
  return valueFromLayout;
985
1138
  };
986
- var hydrateDocumentWithLayoutRelations = (document, layout) => {
1139
+ var hydrateDocumentWithLayoutRelations = (document, layout, hydrationMap = defaultRelationshipHydrationMap) => {
987
1140
  const nextNodes = document.nodes.map((node, index) => {
988
1141
  const layoutBlock = layout[index];
989
1142
  if (!isRecord4(layoutBlock)) {
@@ -992,17 +1145,18 @@ var hydrateDocumentWithLayoutRelations = (document, layout) => {
992
1145
  if (typeof layoutBlock.blockType !== "string" || layoutBlock.blockType !== node.type) {
993
1146
  return node;
994
1147
  }
995
- const nextData = { ...node.data };
996
- if (node.type === "hero") {
997
- nextData.media = hydrateRelationship(nextData.media, layoutBlock.media);
1148
+ const rule = hydrationMap[node.type];
1149
+ if (!rule) {
1150
+ return node;
998
1151
  }
999
- if (node.type === "media") {
1000
- 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]);
1001
1155
  }
1002
- if (node.type === "featureGrid" || node.type === "logoWall" || node.type === "beforeAfter") {
1003
- const studioItems = Array.isArray(nextData.items) ? nextData.items : [];
1004
- const layoutItems = Array.isArray(layoutBlock.items) ? layoutBlock.items : [];
1005
- 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) => {
1006
1160
  if (!isRecord4(rawStudioItem)) {
1007
1161
  return rawStudioItem;
1008
1162
  }
@@ -1011,9 +1165,9 @@ var hydrateDocumentWithLayoutRelations = (document, layout) => {
1011
1165
  return rawStudioItem;
1012
1166
  }
1013
1167
  const nextItem = { ...rawStudioItem };
1014
- nextItem.media = hydrateRelationship(nextItem.media, layoutItem.media);
1015
- nextItem.beforeMedia = hydrateRelationship(nextItem.beforeMedia, layoutItem.beforeMedia);
1016
- nextItem.afterMedia = hydrateRelationship(nextItem.afterMedia, layoutItem.afterMedia);
1168
+ for (const itemField of itemFields) {
1169
+ nextItem[itemField] = hydrateRelationship(nextItem[itemField], layoutItem[itemField]);
1170
+ }
1017
1171
  return nextItem;
1018
1172
  });
1019
1173
  }
@@ -1032,7 +1186,12 @@ var normalizeDocument = (document) => ({
1032
1186
  schemaVersion: 1,
1033
1187
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1034
1188
  });
1035
- 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());
1036
1195
  const nodes = document.nodes.map((node) => {
1037
1196
  if (node.type !== "hero" || !isRecord4(node.data)) {
1038
1197
  return node;
@@ -1042,7 +1201,7 @@ var normalizeLegacyHeroDefaults = (document) => {
1042
1201
  const sectionBackgroundMode = typeof nextData.sectionBackgroundMode === "string" ? nextData.sectionBackgroundMode : "none";
1043
1202
  const hasBackgroundImageURL = typeof nextData.backgroundImageURL === "string" && nextData.backgroundImageURL.trim().length > 0;
1044
1203
  const mediaRelation = getRelationID(nextData.media);
1045
- if (heroBackgroundColor === "#124a37" && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
1204
+ if (normalizedLegacyColors.includes(heroBackgroundColor) && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
1046
1205
  nextData.backgroundColor = "";
1047
1206
  }
1048
1207
  return {
@@ -1061,13 +1220,13 @@ var assertCanPublish = (issues) => {
1061
1220
  throw new Error(`Cannot publish page: ${publishErrors[0].message}`);
1062
1221
  }
1063
1222
  };
1064
- var getStudioDocumentFromPage = (doc) => {
1223
+ var getStudioDocumentFromPage = (doc, relationshipHydration = defaultRelationshipHydrationMap) => {
1065
1224
  const title = typeof doc.title === "string" ? doc.title : void 0;
1066
1225
  const layout = asLayoutArray(doc.layout);
1067
1226
  try {
1068
1227
  const studioDocument = assertStudioDocumentV1(doc.studioDocument);
1069
1228
  if (layout.length > 0) {
1070
- return hydrateDocumentWithLayoutRelations(studioDocument, layout);
1229
+ return hydrateDocumentWithLayoutRelations(studioDocument, layout, relationshipHydration);
1071
1230
  }
1072
1231
  return studioDocument;
1073
1232
  } catch {
@@ -1079,8 +1238,10 @@ var getStudioDocumentFromPage = (doc) => {
1079
1238
  };
1080
1239
  var createStudioPageService = ({
1081
1240
  collectionSlug = "pages",
1241
+ legacyHeroBackgroundColors = DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
1082
1242
  modules,
1083
1243
  payload,
1244
+ relationshipHydration = defaultRelationshipHydrationMap,
1084
1245
  user
1085
1246
  }) => ({
1086
1247
  getPageForStudio: async (pageID) => {
@@ -1092,7 +1253,7 @@ var createStudioPageService = ({
1092
1253
  overrideAccess: false,
1093
1254
  user
1094
1255
  });
1095
- const studioDocument = getStudioDocumentFromPage(page);
1256
+ const studioDocument = getStudioDocumentFromPage(page, relationshipHydration);
1096
1257
  return {
1097
1258
  id: String(page.id),
1098
1259
  slug: typeof page.slug === "string" ? page.slug : "",
@@ -1102,7 +1263,10 @@ var createStudioPageService = ({
1102
1263
  },
1103
1264
  validateStudioDocument: (document) => validateStudioDocument(document, modules),
1104
1265
  saveDraft: async (pageID, document, _metadata) => {
1105
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
1266
+ const normalizedDocument = normalizeLegacyHeroDefaults(
1267
+ normalizeDocument(document),
1268
+ legacyHeroBackgroundColors
1269
+ );
1106
1270
  const compileResult = compileStudioDocument(normalizedDocument, modules);
1107
1271
  await payload.update({
1108
1272
  collection: collectionSlug,
@@ -1123,7 +1287,10 @@ var createStudioPageService = ({
1123
1287
  };
1124
1288
  },
1125
1289
  publish: async (pageID, document, _metadata) => {
1126
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
1290
+ const normalizedDocument = normalizeLegacyHeroDefaults(
1291
+ normalizeDocument(document),
1292
+ legacyHeroBackgroundColors
1293
+ );
1127
1294
  const compileResult = compileStudioDocument(normalizedDocument, modules);
1128
1295
  assertCanPublish(compileResult.issues);
1129
1296
  await payload.update({
@@ -1152,6 +1319,1617 @@ var toEditorInitialDoc = (payloadPage) => ({
1152
1319
  title: payloadPage.title
1153
1320
  });
1154
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
+
1155
2933
  // src/studio-pages/migrations.ts
1156
2934
  var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
1157
2935
  var assertPageStudioDocumentV1 = (value) => {
@@ -1506,9 +3284,13 @@ var pageStudioModuleManifest = {
1506
3284
  };
1507
3285
  // Annotate the CommonJS export names for ESM import in node:
1508
3286
  0 && (module.exports = {
3287
+ DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
1509
3288
  createDefaultStudioDocument,
3289
+ createStudioPageFields,
1510
3290
  createStudioPageService,
3291
+ createStudioPagesCollection,
1511
3292
  defaultBuilderThemeTokens,
3293
+ defaultRelationshipHydrationMap,
1512
3294
  getStudioDocumentFromPage,
1513
3295
  layoutToStudioDocument,
1514
3296
  pageInspectorPanels,