@orion-studios/payload-studio 0.5.0-beta.4 → 0.5.0-beta.40

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 (46) hide show
  1. package/dist/admin/client.js +68 -7
  2. package/dist/admin/client.mjs +68 -7
  3. package/dist/admin/index.d.mts +1 -1
  4. package/dist/admin/index.d.ts +1 -1
  5. package/dist/admin/index.js +37 -0
  6. package/dist/admin/index.mjs +3 -1
  7. package/dist/admin-app/client.d.mts +4 -0
  8. package/dist/admin-app/client.d.ts +4 -0
  9. package/dist/admin-app/client.js +705 -2
  10. package/dist/admin-app/client.mjs +701 -1
  11. package/dist/admin-app/index.d.mts +1 -1
  12. package/dist/admin-app/index.d.ts +1 -1
  13. package/dist/admin-app/index.js +167 -0
  14. package/dist/admin-app/index.mjs +13 -1
  15. package/dist/admin-app/styles.css +127 -0
  16. package/dist/blocks/index.js +424 -4
  17. package/dist/blocks/index.mjs +2 -2
  18. package/dist/{chunk-UJFU323N.mjs → chunk-34J4T7X3.mjs} +33 -9
  19. package/dist/{chunk-J7W5EE3B.mjs → chunk-7IGLXLUB.mjs} +37 -0
  20. package/dist/{chunk-PC5622T7.mjs → chunk-I4NH636V.mjs} +411 -5
  21. package/dist/{chunk-ZLLNO5FM.mjs → chunk-RXXPFQWL.mjs} +44 -13
  22. package/dist/{chunk-ETRRXURT.mjs → chunk-SIL2J5MF.mjs} +14 -0
  23. package/dist/chunk-XVH5SCBD.mjs +234 -0
  24. package/dist/index-BBvk9b9i.d.mts +97 -0
  25. package/dist/index-BBvk9b9i.d.ts +97 -0
  26. package/dist/{index-DbH0Ljwp.d.mts → index-CpG3UHcS.d.mts} +1 -0
  27. package/dist/{index-DbH0Ljwp.d.ts → index-CpG3UHcS.d.ts} +1 -0
  28. package/dist/{index-DJFhANvJ.d.ts → index-Dj21uD_B.d.mts} +5 -2
  29. package/dist/{index-DJFhANvJ.d.mts → index-Dj21uD_B.d.ts} +5 -2
  30. package/dist/index.d.mts +3 -3
  31. package/dist/index.d.ts +3 -3
  32. package/dist/index.js +871 -203
  33. package/dist/index.mjs +11 -11
  34. package/dist/nextjs/index.d.mts +1 -1
  35. package/dist/nextjs/index.d.ts +1 -1
  36. package/dist/nextjs/index.js +290 -13
  37. package/dist/nextjs/index.mjs +4 -1
  38. package/dist/studio-pages/builder.css +25 -1
  39. package/dist/studio-pages/client.js +3545 -1279
  40. package/dist/studio-pages/client.mjs +3545 -1279
  41. package/dist/studio-pages/index.js +33 -8
  42. package/dist/studio-pages/index.mjs +2 -2
  43. package/package.json +24 -12
  44. package/dist/chunk-AAOHJDNS.mjs +0 -67
  45. package/dist/index-BallJs-K.d.mts +0 -43
  46. package/dist/index-BallJs-K.d.ts +0 -43
package/dist/index.js CHANGED
@@ -43,6 +43,7 @@ module.exports = __toCommonJS(index_exports);
43
43
  var admin_exports = {};
44
44
  __export(admin_exports, {
45
45
  configureAdmin: () => configureAdmin,
46
+ createHeaderNavItemsField: () => createHeaderNavItemsField,
46
47
  createThemePreferenceField: () => createThemePreferenceField,
47
48
  themePreferenceField: () => themePreferenceField,
48
49
  withTooltips: () => withTooltips
@@ -306,12 +307,53 @@ function addTooltipToField(field, tooltips) {
306
307
  return field;
307
308
  }
308
309
 
310
+ // src/admin/fields/headerNav.ts
311
+ var createHeaderNavItemsField = () => ({
312
+ name: "navItems",
313
+ type: "array",
314
+ labels: { singular: "Navigation Link", plural: "Navigation Links" },
315
+ admin: {
316
+ description: "The links displayed in your website's main navigation menu."
317
+ },
318
+ fields: [
319
+ {
320
+ name: "label",
321
+ type: "text",
322
+ required: true,
323
+ admin: {
324
+ description: "The text shown for this navigation link."
325
+ }
326
+ },
327
+ {
328
+ name: "href",
329
+ type: "text",
330
+ required: true,
331
+ admin: {
332
+ description: 'The URL this link points to (e.g., "/about" or "https://example.com").'
333
+ }
334
+ },
335
+ {
336
+ name: "parentHref",
337
+ type: "text",
338
+ admin: {
339
+ description: "Optional parent link URL. If set to another nav item href, this item appears in that dropdown."
340
+ }
341
+ }
342
+ ]
343
+ });
344
+
309
345
  // src/admin-app/index.ts
310
346
  var admin_app_exports = {};
311
347
  __export(admin_app_exports, {
312
348
  AdminBreadcrumbs: () => AdminBreadcrumbs,
313
349
  AdminPage: () => AdminPage,
350
+ buildAdminPageLinkOptions: () => buildAdminPageLinkOptions,
351
+ buildNestedNavTree: () => buildNestedNavTree,
352
+ getAdminNavRows: () => getAdminNavRows,
314
353
  navItemIsActive: () => navItemIsActive,
354
+ normalizeAdminNavInputs: () => normalizeAdminNavInputs,
355
+ normalizeNestedNavItems: () => normalizeNestedNavItems,
356
+ parseAdminHeaderNavFromForm: () => parseAdminHeaderNavFromForm,
315
357
  roleCanAccessNav: () => roleCanAccessNav
316
358
  });
317
359
 
@@ -345,6 +387,161 @@ function AdminPage({ title, description, breadcrumbs, actions, children }) {
345
387
  ] });
346
388
  }
347
389
 
390
+ // src/admin-app/nestedNavigation.ts
391
+ var normalizeNestedNavItems = (items) => {
392
+ const deduped = [];
393
+ const seen = /* @__PURE__ */ new Set();
394
+ for (const item of items) {
395
+ const href = typeof item.href === "string" ? item.href.trim() : "";
396
+ const label = typeof item.label === "string" ? item.label.trim() : "";
397
+ const parentHref = typeof item.parentHref === "string" ? item.parentHref.trim() : "";
398
+ if (!href || !label || seen.has(href)) {
399
+ continue;
400
+ }
401
+ seen.add(href);
402
+ deduped.push({
403
+ href,
404
+ label,
405
+ ...parentHref ? { parentHref } : {}
406
+ });
407
+ }
408
+ const hrefs = new Set(deduped.map((item) => item.href));
409
+ return deduped.map((item) => ({
410
+ href: item.href,
411
+ label: item.label,
412
+ ...item.parentHref && item.parentHref !== item.href && hrefs.has(item.parentHref) ? { parentHref: item.parentHref } : {}
413
+ }));
414
+ };
415
+ var buildNestedNavTree = (items) => {
416
+ const childrenByParent = /* @__PURE__ */ new Map();
417
+ const topLevel = [];
418
+ for (const item of items) {
419
+ if (!item.parentHref) {
420
+ topLevel.push(item);
421
+ continue;
422
+ }
423
+ const children = childrenByParent.get(item.parentHref) || [];
424
+ children.push(item);
425
+ childrenByParent.set(item.parentHref, children);
426
+ }
427
+ if (topLevel.length === 0 && items.length > 0) {
428
+ return {
429
+ topLevel: items.map((item) => ({ href: item.href, label: item.label })),
430
+ childrenByParent: /* @__PURE__ */ new Map()
431
+ };
432
+ }
433
+ return { childrenByParent, topLevel };
434
+ };
435
+
436
+ // src/admin-app/navigationLinks.ts
437
+ var fallbackHomeOption = {
438
+ href: "/",
439
+ label: "Home",
440
+ title: "Home"
441
+ };
442
+ var buildAdminPageLinkOptions = (pages) => {
443
+ const options = pages.map((page) => {
444
+ const href = typeof page.path === "string" ? page.path.trim() : "";
445
+ const title = typeof page.title === "string" && page.title.trim().length > 0 ? page.title.trim() : "Untitled Page";
446
+ if (!href) {
447
+ return null;
448
+ }
449
+ const depth = href === "/" ? 0 : href.split("/").filter(Boolean).length;
450
+ const indentLevel = Math.max(depth - 1, 0);
451
+ const indent = indentLevel > 0 ? `${"\u21B3 ".repeat(indentLevel)}` : "";
452
+ return {
453
+ href,
454
+ label: `${indent}${title}`,
455
+ title
456
+ };
457
+ }).filter((option) => option !== null).sort((a, b) => {
458
+ if (a.href === "/" && b.href !== "/") return -1;
459
+ if (b.href === "/" && a.href !== "/") return 1;
460
+ return a.href.localeCompare(b.href);
461
+ });
462
+ if (options.length === 0) {
463
+ return [fallbackHomeOption];
464
+ }
465
+ return options;
466
+ };
467
+ var getAdminNavRows = (navItems, pageOptions, minRows = 6, maxRows = 20) => {
468
+ const preferredRows = Math.max(navItems.length + 2, Math.min(8, pageOptions.length));
469
+ return Math.min(Math.max(minRows, preferredRows), maxRows);
470
+ };
471
+ var normalizeAdminNavInputs = (rows, pageOptions) => {
472
+ const allowedLinks = new Map(pageOptions.map((option) => [option.href, option.title]));
473
+ const deduped = [];
474
+ const seen = /* @__PURE__ */ new Set();
475
+ for (const row of rows) {
476
+ const href = typeof row.href === "string" ? row.href.trim() : "";
477
+ const defaultLabel = allowedLinks.get(href);
478
+ const explicitLabel = typeof row.label === "string" ? row.label.trim() : "";
479
+ const parentHref = typeof row.parentHref === "string" ? row.parentHref.trim() : "";
480
+ if (!href || !defaultLabel || seen.has(href)) {
481
+ continue;
482
+ }
483
+ seen.add(href);
484
+ deduped.push({
485
+ href,
486
+ label: explicitLabel.length > 0 ? explicitLabel : defaultLabel,
487
+ ...parentHref.length > 0 ? { parentHref } : {}
488
+ });
489
+ }
490
+ const hrefs = new Set(deduped.map((item) => item.href));
491
+ const parentByHref = new Map(deduped.map((item) => [item.href, item.parentHref || ""]));
492
+ const createsCycle = (href, parentHref) => {
493
+ const visited = /* @__PURE__ */ new Set([href]);
494
+ let current = parentHref;
495
+ while (current) {
496
+ if (visited.has(current)) {
497
+ return true;
498
+ }
499
+ visited.add(current);
500
+ current = parentByHref.get(current) || "";
501
+ }
502
+ return false;
503
+ };
504
+ return deduped.map((item) => {
505
+ const parentHref = item.parentHref;
506
+ if (!parentHref || parentHref === item.href || !hrefs.has(parentHref) || createsCycle(item.href, parentHref)) {
507
+ return {
508
+ href: item.href,
509
+ label: item.label
510
+ };
511
+ }
512
+ return item;
513
+ });
514
+ };
515
+ var parseAdminHeaderNavFromForm = (formData, pageOptions, maxRows = 24) => {
516
+ const serialized = String(formData.get("navItemsState") || "").trim();
517
+ if (serialized.length > 0) {
518
+ try {
519
+ const parsed = JSON.parse(serialized);
520
+ if (Array.isArray(parsed)) {
521
+ return normalizeAdminNavInputs(parsed.slice(0, maxRows), pageOptions);
522
+ }
523
+ } catch {
524
+ }
525
+ }
526
+ const rawCount = Number(String(formData.get("navCount") || "0"));
527
+ const navCount = Number.isFinite(rawCount) ? Math.max(0, Math.min(rawCount, maxRows)) : 0;
528
+ const navRows = [];
529
+ for (let index = 0; index < navCount; index += 1) {
530
+ const href = String(formData.get(`navPage_${index}`) || "").trim();
531
+ const label = String(formData.get(`navLabel_${index}`) || "").trim();
532
+ const parentHref = String(formData.get(`navParentHref_${index}`) || "").trim();
533
+ navRows.push({
534
+ href,
535
+ label,
536
+ parentHref
537
+ });
538
+ }
539
+ if (navRows.length > 0) {
540
+ return normalizeAdminNavInputs(navRows, pageOptions);
541
+ }
542
+ return [];
543
+ };
544
+
348
545
  // src/admin-app/routeRegistry.ts
349
546
  var roleCanAccessNav = (role, item) => {
350
547
  if (!item.roles || item.roles.length === 0) {
@@ -400,6 +597,7 @@ var sectionStyleDefaults = {
400
597
  contentGradientPreset: "none",
401
598
  contentGradientTo: "#f4f6f2",
402
599
  contentWidth: "inherit",
600
+ sectionPaddingX: "inherit",
403
601
  sectionBackgroundColor: "#ffffff",
404
602
  sectionBackgroundMode: "none",
405
603
  sectionGradientAngle: "135",
@@ -437,6 +635,19 @@ var sectionStyleFields = () => [
437
635
  type: "select",
438
636
  defaultValue: sectionStyleDefaults.sectionPaddingY,
439
637
  options: [
638
+ { label: "None", value: "none" },
639
+ { label: "Small", value: "sm" },
640
+ { label: "Medium", value: "md" },
641
+ { label: "Large", value: "lg" }
642
+ ]
643
+ },
644
+ {
645
+ name: "sectionPaddingX",
646
+ type: "select",
647
+ defaultValue: sectionStyleDefaults.sectionPaddingX,
648
+ options: [
649
+ { label: "Inherit", value: "inherit" },
650
+ { label: "None", value: "none" },
440
651
  { label: "Small", value: "sm" },
441
652
  { label: "Medium", value: "md" },
442
653
  { label: "Large", value: "lg" }
@@ -575,6 +786,67 @@ var BeforeAfterBlock = {
575
786
  relationTo: "media",
576
787
  required: false
577
788
  },
789
+ {
790
+ name: "imageHeight",
791
+ type: "number",
792
+ defaultValue: 160,
793
+ min: 60,
794
+ max: 600,
795
+ admin: {
796
+ description: "Overrides the before/after image height (in pixels).",
797
+ step: 10
798
+ }
799
+ },
800
+ {
801
+ name: "imageFit",
802
+ type: "select",
803
+ defaultValue: "cover",
804
+ options: [
805
+ { label: "Cover", value: "cover" },
806
+ { label: "Contain", value: "contain" }
807
+ ]
808
+ },
809
+ {
810
+ name: "imageCornerStyle",
811
+ type: "select",
812
+ defaultValue: "rounded",
813
+ options: [
814
+ { label: "Rounded", value: "rounded" },
815
+ { label: "Square", value: "square" }
816
+ ]
817
+ },
818
+ {
819
+ name: "imagePosition",
820
+ type: "select",
821
+ defaultValue: "center",
822
+ options: [
823
+ { label: "Center", value: "center" },
824
+ { label: "Top", value: "top" },
825
+ { label: "Bottom", value: "bottom" },
826
+ { label: "Left", value: "left" },
827
+ { label: "Right", value: "right" }
828
+ ]
829
+ },
830
+ {
831
+ name: "imagePositionX",
832
+ type: "number",
833
+ min: 0,
834
+ max: 100,
835
+ admin: {
836
+ description: "Optional custom horizontal focus (0-100). Overrides Image Position when set.",
837
+ step: 1
838
+ }
839
+ },
840
+ {
841
+ name: "imagePositionY",
842
+ type: "number",
843
+ min: 0,
844
+ max: 100,
845
+ admin: {
846
+ description: "Optional custom vertical focus (0-100). Overrides Image Position when set.",
847
+ step: 1
848
+ }
849
+ },
578
850
  {
579
851
  name: "description",
580
852
  type: "textarea"
@@ -772,6 +1044,67 @@ var FeatureGridBlock = {
772
1044
  type: "upload",
773
1045
  relationTo: "media",
774
1046
  required: false
1047
+ },
1048
+ {
1049
+ name: "imageHeight",
1050
+ type: "number",
1051
+ defaultValue: 160,
1052
+ min: 40,
1053
+ max: 600,
1054
+ admin: {
1055
+ description: "Overrides the image height in the builder/section (in pixels).",
1056
+ step: 10
1057
+ }
1058
+ },
1059
+ {
1060
+ name: "imageFit",
1061
+ type: "select",
1062
+ defaultValue: "cover",
1063
+ options: [
1064
+ { label: "Cover", value: "cover" },
1065
+ { label: "Contain", value: "contain" }
1066
+ ]
1067
+ },
1068
+ {
1069
+ name: "imageCornerStyle",
1070
+ type: "select",
1071
+ defaultValue: "rounded",
1072
+ options: [
1073
+ { label: "Rounded", value: "rounded" },
1074
+ { label: "Square", value: "square" }
1075
+ ]
1076
+ },
1077
+ {
1078
+ name: "imagePosition",
1079
+ type: "select",
1080
+ defaultValue: "center",
1081
+ options: [
1082
+ { label: "Center", value: "center" },
1083
+ { label: "Top", value: "top" },
1084
+ { label: "Bottom", value: "bottom" },
1085
+ { label: "Left", value: "left" },
1086
+ { label: "Right", value: "right" }
1087
+ ]
1088
+ },
1089
+ {
1090
+ name: "imagePositionX",
1091
+ type: "number",
1092
+ min: 0,
1093
+ max: 100,
1094
+ admin: {
1095
+ description: "Optional custom horizontal focus (0-100). Overrides Image Position when set.",
1096
+ step: 1
1097
+ }
1098
+ },
1099
+ {
1100
+ name: "imagePositionY",
1101
+ type: "number",
1102
+ min: 0,
1103
+ max: 100,
1104
+ admin: {
1105
+ description: "Optional custom vertical focus (0-100). Overrides Image Position when set.",
1106
+ step: 1
1107
+ }
775
1108
  }
776
1109
  ]
777
1110
  },
@@ -898,10 +1231,44 @@ var HeroBlock = {
898
1231
  relationTo: "media"
899
1232
  },
900
1233
  {
901
- name: "backgroundImageURL",
902
- type: "text",
1234
+ name: "backgroundImageFit",
1235
+ type: "select",
1236
+ defaultValue: "cover",
1237
+ options: [
1238
+ { label: "Cover", value: "cover" },
1239
+ { label: "Cover (Square)", value: "cover-square" },
1240
+ { label: "Contain", value: "contain" },
1241
+ { label: "Contain (Square)", value: "contain-square" }
1242
+ ],
1243
+ admin: {
1244
+ description: "How the hero image should be sized within the section."
1245
+ }
1246
+ },
1247
+ {
1248
+ name: "backgroundImageCornerStyle",
1249
+ type: "select",
1250
+ defaultValue: "rounded",
1251
+ options: [
1252
+ { label: "Rounded", value: "rounded" },
1253
+ { label: "Square", value: "square" }
1254
+ ],
903
1255
  admin: {
904
- description: "Optional external/background image URL override for this hero section."
1256
+ description: "How the hero image corners should appear."
1257
+ }
1258
+ },
1259
+ {
1260
+ name: "backgroundImagePosition",
1261
+ type: "select",
1262
+ defaultValue: "center",
1263
+ options: [
1264
+ { label: "Center", value: "center" },
1265
+ { label: "Top", value: "top" },
1266
+ { label: "Bottom", value: "bottom" },
1267
+ { label: "Left", value: "left" },
1268
+ { label: "Right", value: "right" }
1269
+ ],
1270
+ admin: {
1271
+ description: "Where the hero image should anchor inside the section."
905
1272
  }
906
1273
  },
907
1274
  {
@@ -911,6 +1278,113 @@ var HeroBlock = {
911
1278
  description: "Optional background color override (example: #124a37)."
912
1279
  }
913
1280
  },
1281
+ {
1282
+ name: "backgroundOverlayMode",
1283
+ type: "select",
1284
+ defaultValue: "none",
1285
+ options: [
1286
+ { label: "None", value: "none" },
1287
+ { label: "Solid", value: "solid" },
1288
+ { label: "Gradient", value: "gradient" }
1289
+ ],
1290
+ admin: {
1291
+ description: "Optional overlay on top of the hero image (applies when an image is present)."
1292
+ }
1293
+ },
1294
+ {
1295
+ name: "backgroundOverlayOpacity",
1296
+ type: "number",
1297
+ defaultValue: 45,
1298
+ min: 0,
1299
+ max: 100,
1300
+ admin: {
1301
+ description: "Overlay opacity (0-100).",
1302
+ step: 1
1303
+ }
1304
+ },
1305
+ {
1306
+ name: "backgroundOverlayColor",
1307
+ type: "text",
1308
+ admin: {
1309
+ description: "Overlay solid color (example: #000000). Used when Overlay Mode is Solid."
1310
+ }
1311
+ },
1312
+ {
1313
+ name: "backgroundOverlayGradientFrom",
1314
+ type: "text",
1315
+ admin: {
1316
+ description: "Gradient overlay start color (example: #0d4a37). Used when Overlay Mode is Gradient."
1317
+ }
1318
+ },
1319
+ {
1320
+ name: "backgroundOverlayGradientTo",
1321
+ type: "text",
1322
+ admin: {
1323
+ description: "Gradient overlay end color (example: #1f684f). Used when Overlay Mode is Gradient."
1324
+ }
1325
+ },
1326
+ {
1327
+ name: "backgroundOverlayGradientAngle",
1328
+ type: "text",
1329
+ admin: {
1330
+ description: "Gradient overlay angle in degrees (0-360). Used when Overlay Mode is Gradient."
1331
+ }
1332
+ },
1333
+ {
1334
+ name: "backgroundOverlayGradientFromStrength",
1335
+ type: "number",
1336
+ defaultValue: 100,
1337
+ min: 0,
1338
+ max: 100,
1339
+ admin: {
1340
+ description: "Gradient start strength (0-100). Set to 0 for transparent.",
1341
+ step: 1
1342
+ }
1343
+ },
1344
+ {
1345
+ name: "backgroundOverlayGradientToStrength",
1346
+ type: "number",
1347
+ defaultValue: 100,
1348
+ min: 0,
1349
+ max: 100,
1350
+ admin: {
1351
+ description: "Gradient end strength (0-100). Set to 0 for transparent.",
1352
+ step: 1
1353
+ }
1354
+ },
1355
+ {
1356
+ name: "backgroundOverlayGradientStart",
1357
+ type: "number",
1358
+ defaultValue: 0,
1359
+ min: 0,
1360
+ max: 100,
1361
+ admin: {
1362
+ description: "Where the gradient starts (0-100).",
1363
+ step: 1
1364
+ }
1365
+ },
1366
+ {
1367
+ name: "backgroundOverlayGradientEnd",
1368
+ type: "number",
1369
+ defaultValue: 100,
1370
+ min: 0,
1371
+ max: 100,
1372
+ admin: {
1373
+ description: "Where the gradient ends (0-100).",
1374
+ step: 1
1375
+ }
1376
+ },
1377
+ {
1378
+ name: "backgroundOverlayGradientFeather",
1379
+ type: "number",
1380
+ defaultValue: 100,
1381
+ min: 0,
1382
+ max: 100,
1383
+ admin: {
1384
+ description: "How soft the transition is (0 = hard edge, 100 = smooth).",
1385
+ step: 1
1386
+ }
1387
+ },
914
1388
  {
915
1389
  name: "variant",
916
1390
  type: "select",
@@ -926,6 +1400,28 @@ var HeroBlock = {
926
1400
  }
927
1401
  ]
928
1402
  },
1403
+ {
1404
+ name: "heroHeight",
1405
+ type: "select",
1406
+ defaultValue: "sm",
1407
+ options: [
1408
+ {
1409
+ label: "Small",
1410
+ value: "sm"
1411
+ },
1412
+ {
1413
+ label: "Medium (Half Screen)",
1414
+ value: "md"
1415
+ },
1416
+ {
1417
+ label: "Full Screen",
1418
+ value: "full"
1419
+ }
1420
+ ],
1421
+ admin: {
1422
+ description: "Controls the vertical height of the hero section."
1423
+ }
1424
+ },
929
1425
  ...sectionStyleFields()
930
1426
  ]
931
1427
  };
@@ -971,6 +1467,67 @@ var LogoWallBlock = {
971
1467
  relationTo: "media",
972
1468
  required: false
973
1469
  },
1470
+ {
1471
+ name: "imageHeight",
1472
+ type: "number",
1473
+ defaultValue: 64,
1474
+ min: 24,
1475
+ max: 200,
1476
+ admin: {
1477
+ description: "Overrides the logo image height (in pixels).",
1478
+ step: 4
1479
+ }
1480
+ },
1481
+ {
1482
+ name: "imageFit",
1483
+ type: "select",
1484
+ defaultValue: "contain",
1485
+ options: [
1486
+ { label: "Cover", value: "cover" },
1487
+ { label: "Contain", value: "contain" }
1488
+ ]
1489
+ },
1490
+ {
1491
+ name: "imageCornerStyle",
1492
+ type: "select",
1493
+ defaultValue: "rounded",
1494
+ options: [
1495
+ { label: "Rounded", value: "rounded" },
1496
+ { label: "Square", value: "square" }
1497
+ ]
1498
+ },
1499
+ {
1500
+ name: "imagePosition",
1501
+ type: "select",
1502
+ defaultValue: "center",
1503
+ options: [
1504
+ { label: "Center", value: "center" },
1505
+ { label: "Top", value: "top" },
1506
+ { label: "Bottom", value: "bottom" },
1507
+ { label: "Left", value: "left" },
1508
+ { label: "Right", value: "right" }
1509
+ ]
1510
+ },
1511
+ {
1512
+ name: "imagePositionX",
1513
+ type: "number",
1514
+ min: 0,
1515
+ max: 100,
1516
+ admin: {
1517
+ description: "Optional custom horizontal focus (0-100). Overrides Image Position when set.",
1518
+ step: 1
1519
+ }
1520
+ },
1521
+ {
1522
+ name: "imagePositionY",
1523
+ type: "number",
1524
+ min: 0,
1525
+ max: 100,
1526
+ admin: {
1527
+ description: "Optional custom vertical focus (0-100). Overrides Image Position when set.",
1528
+ step: 1
1529
+ }
1530
+ },
974
1531
  {
975
1532
  name: "href",
976
1533
  type: "text"
@@ -1021,6 +1578,36 @@ var MediaBlock = {
1021
1578
  }
1022
1579
  ]
1023
1580
  },
1581
+ {
1582
+ name: "imageFit",
1583
+ type: "select",
1584
+ defaultValue: "cover",
1585
+ options: [
1586
+ { label: "Cover", value: "cover" },
1587
+ { label: "Contain", value: "contain" }
1588
+ ]
1589
+ },
1590
+ {
1591
+ name: "imageCornerStyle",
1592
+ type: "select",
1593
+ defaultValue: "rounded",
1594
+ options: [
1595
+ { label: "Rounded", value: "rounded" },
1596
+ { label: "Square", value: "square" }
1597
+ ]
1598
+ },
1599
+ {
1600
+ name: "imagePosition",
1601
+ type: "select",
1602
+ defaultValue: "center",
1603
+ options: [
1604
+ { label: "Center", value: "center" },
1605
+ { label: "Top", value: "top" },
1606
+ { label: "Bottom", value: "bottom" },
1607
+ { label: "Left", value: "left" },
1608
+ { label: "Right", value: "right" }
1609
+ ]
1610
+ },
1024
1611
  ...sectionStyleFields()
1025
1612
  ]
1026
1613
  };
@@ -1138,11 +1725,41 @@ var TestimonialsBlock = {
1138
1725
  type: "text",
1139
1726
  required: true
1140
1727
  },
1728
+ {
1729
+ name: "visibleCount",
1730
+ type: "number",
1731
+ defaultValue: 3,
1732
+ min: 1,
1733
+ max: 6,
1734
+ admin: {
1735
+ description: "How many testimonials to show at once.",
1736
+ step: 1
1737
+ }
1738
+ },
1739
+ {
1740
+ name: "autoRotate",
1741
+ type: "checkbox",
1742
+ defaultValue: true,
1743
+ admin: {
1744
+ description: "Automatically rotates through all testimonials."
1745
+ }
1746
+ },
1747
+ {
1748
+ name: "rotateIntervalSeconds",
1749
+ type: "number",
1750
+ defaultValue: 7,
1751
+ min: 2,
1752
+ max: 30,
1753
+ admin: {
1754
+ description: "How often to rotate (in seconds).",
1755
+ step: 1
1756
+ }
1757
+ },
1141
1758
  {
1142
1759
  name: "items",
1143
1760
  type: "array",
1144
1761
  minRows: 1,
1145
- maxRows: 6,
1762
+ maxRows: 30,
1146
1763
  fields: [
1147
1764
  {
1148
1765
  name: "quote",
@@ -1510,197 +2127,6 @@ function createPayloadClient(config) {
1510
2127
 
1511
2128
  // src/nextjs/queries/pages.ts
1512
2129
  var import_cache = require("next/cache");
1513
- function normalizePath(segments) {
1514
- if (!segments || segments.length === 0) {
1515
- return "/";
1516
- }
1517
- const cleaned = segments.map((segment) => segment.trim()).filter(Boolean).join("/");
1518
- return cleaned.length > 0 ? `/${cleaned}` : "/";
1519
- }
1520
- async function queryPageByPath(payload, path2, draft) {
1521
- const result = await payload.find({
1522
- collection: "pages",
1523
- depth: 2,
1524
- draft,
1525
- limit: 1,
1526
- overrideAccess: false,
1527
- where: {
1528
- path: {
1529
- equals: path2
1530
- }
1531
- }
1532
- });
1533
- if (result.docs.length > 0) {
1534
- return result.docs[0] || null;
1535
- }
1536
- if (path2 === "/") {
1537
- const homeResult = await payload.find({
1538
- collection: "pages",
1539
- depth: 2,
1540
- draft,
1541
- limit: 1,
1542
- overrideAccess: false,
1543
- where: {
1544
- slug: {
1545
- equals: "home"
1546
- }
1547
- }
1548
- });
1549
- return homeResult.docs[0] || null;
1550
- }
1551
- return null;
1552
- }
1553
- function createPageQueries(getPayloadClient, contentTag = "website-content") {
1554
- const getPublishedPageByPathCached = (0, import_cache.unstable_cache)(
1555
- async (path2) => {
1556
- const payload = await getPayloadClient();
1557
- return queryPageByPath(payload, path2, false);
1558
- },
1559
- ["page-by-path"],
1560
- { tags: [contentTag] }
1561
- );
1562
- async function getPageBySegments(segments, draft = false) {
1563
- const path2 = normalizePath(segments);
1564
- const payload = await getPayloadClient();
1565
- if (draft) {
1566
- return queryPageByPath(payload, path2, true);
1567
- }
1568
- return getPublishedPageByPathCached(path2);
1569
- }
1570
- async function listPublishedPagePaths() {
1571
- const payload = await getPayloadClient();
1572
- const pages = await payload.find({
1573
- collection: "pages",
1574
- depth: 0,
1575
- draft: false,
1576
- limit: 1e3,
1577
- pagination: false,
1578
- overrideAccess: false,
1579
- where: {
1580
- _status: {
1581
- equals: "published"
1582
- }
1583
- }
1584
- });
1585
- return pages.docs.map((doc) => doc.path).filter((path2) => typeof path2 === "string" && path2.length > 0);
1586
- }
1587
- function pathToSegments(path2) {
1588
- if (!path2 || path2 === "/") {
1589
- return [];
1590
- }
1591
- return path2.split("/").filter(Boolean);
1592
- }
1593
- return {
1594
- getPageBySegments,
1595
- listPublishedPagePaths,
1596
- pathToSegments
1597
- };
1598
- }
1599
-
1600
- // src/nextjs/queries/site.ts
1601
- var import_cache2 = require("next/cache");
1602
- function createSiteQueries(getPayloadClient, contentTag = "website-content") {
1603
- const getSiteSettingsCached = (0, import_cache2.unstable_cache)(
1604
- async () => {
1605
- const payload = await getPayloadClient();
1606
- const settings = await payload.findGlobal({
1607
- slug: "site-settings",
1608
- depth: 1
1609
- });
1610
- return settings;
1611
- },
1612
- ["site-settings-global"],
1613
- { tags: [contentTag] }
1614
- );
1615
- const getHeaderCached = (0, import_cache2.unstable_cache)(
1616
- async () => {
1617
- const payload = await getPayloadClient();
1618
- const header = await payload.findGlobal({
1619
- slug: "header",
1620
- depth: 1
1621
- });
1622
- return header;
1623
- },
1624
- ["header-global"],
1625
- { tags: [contentTag] }
1626
- );
1627
- const getFooterCached = (0, import_cache2.unstable_cache)(
1628
- async () => {
1629
- const payload = await getPayloadClient();
1630
- const footer = await payload.findGlobal({
1631
- slug: "footer",
1632
- depth: 1
1633
- });
1634
- return footer;
1635
- },
1636
- ["footer-global"],
1637
- { tags: [contentTag] }
1638
- );
1639
- async function getSiteSettings(draft = false) {
1640
- if (draft) {
1641
- const payload = await getPayloadClient();
1642
- const settings = await payload.findGlobal({
1643
- slug: "site-settings",
1644
- depth: 1,
1645
- draft: true
1646
- });
1647
- return settings;
1648
- }
1649
- return getSiteSettingsCached();
1650
- }
1651
- async function getHeader(draft = false) {
1652
- if (draft) {
1653
- const payload = await getPayloadClient();
1654
- const header = await payload.findGlobal({
1655
- slug: "header",
1656
- depth: 1,
1657
- draft: true
1658
- });
1659
- return header;
1660
- }
1661
- return getHeaderCached();
1662
- }
1663
- async function getFooter(draft = false) {
1664
- if (draft) {
1665
- const payload = await getPayloadClient();
1666
- const footer = await payload.findGlobal({
1667
- slug: "footer",
1668
- depth: 1,
1669
- draft: true
1670
- });
1671
- return footer;
1672
- }
1673
- return getFooterCached();
1674
- }
1675
- return {
1676
- getSiteSettings,
1677
- getHeader,
1678
- getFooter
1679
- };
1680
- }
1681
-
1682
- // src/nextjs/utilities/media.ts
1683
- function resolveMedia(media) {
1684
- if (!media) {
1685
- return null;
1686
- }
1687
- if (typeof media === "number" || typeof media === "string") {
1688
- return null;
1689
- }
1690
- if (media.url) {
1691
- return {
1692
- url: media.url,
1693
- alt: media.alt || ""
1694
- };
1695
- }
1696
- if (media.filename) {
1697
- return {
1698
- url: `/media/${media.filename}`,
1699
- alt: media.alt || ""
1700
- };
1701
- }
1702
- return null;
1703
- }
1704
2130
 
1705
2131
  // src/studio/index.ts
1706
2132
  var studio_exports = {};
@@ -1872,6 +2298,9 @@ var defaultNodeData = {
1872
2298
  items: [
1873
2299
  {
1874
2300
  description: "Before and after result summary.",
2301
+ imageCornerStyle: "rounded",
2302
+ imageFit: "cover",
2303
+ imagePosition: "center",
1875
2304
  label: "Project One"
1876
2305
  }
1877
2306
  ],
@@ -1895,9 +2324,9 @@ var defaultNodeData = {
1895
2324
  featureGrid: {
1896
2325
  ...withSectionStyleDefaults({}),
1897
2326
  items: [
1898
- { description: "Explain this point.", icon: "01", title: "Feature One" },
1899
- { description: "Explain this point.", icon: "02", title: "Feature Two" },
1900
- { description: "Explain this point.", icon: "03", title: "Feature Three" }
2327
+ { description: "Explain this point.", icon: "01", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature One" },
2328
+ { description: "Explain this point.", icon: "02", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature Two" },
2329
+ { description: "Explain this point.", icon: "03", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature Three" }
1901
2330
  ],
1902
2331
  title: "Section Title",
1903
2332
  variant: "cards"
@@ -1910,7 +2339,22 @@ var defaultNodeData = {
1910
2339
  },
1911
2340
  hero: {
1912
2341
  ...withSectionStyleDefaults({}),
1913
- backgroundColor: "#124a37",
2342
+ backgroundColor: "",
2343
+ backgroundOverlayMode: "none",
2344
+ backgroundOverlayOpacity: 45,
2345
+ backgroundOverlayColor: "#000000",
2346
+ backgroundOverlayGradientFrom: "#0d4a37",
2347
+ backgroundOverlayGradientTo: "#1f684f",
2348
+ backgroundOverlayGradientAngle: "135",
2349
+ backgroundOverlayGradientFromStrength: 100,
2350
+ backgroundOverlayGradientToStrength: 100,
2351
+ backgroundOverlayGradientStart: 0,
2352
+ backgroundOverlayGradientEnd: 100,
2353
+ backgroundOverlayGradientFeather: 100,
2354
+ backgroundImageCornerStyle: "rounded",
2355
+ backgroundImageFit: "cover",
2356
+ backgroundImagePosition: "center",
2357
+ heroHeight: "sm",
1914
2358
  headline: "New Hero Section",
1915
2359
  kicker: "Optional kicker",
1916
2360
  primaryHref: "/contact",
@@ -1923,13 +2367,16 @@ var defaultNodeData = {
1923
2367
  media: {
1924
2368
  ...withSectionStyleDefaults({}),
1925
2369
  caption: "Add a caption",
2370
+ imageCornerStyle: "rounded",
2371
+ imageFit: "cover",
2372
+ imagePosition: "center",
1926
2373
  size: "default"
1927
2374
  },
1928
2375
  logoWall: withSectionStyleDefaults({
1929
2376
  items: [
1930
- { name: "Trusted Partner 1" },
1931
- { name: "Trusted Partner 2" },
1932
- { name: "Trusted Partner 3" }
2377
+ { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 1" },
2378
+ { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 2" },
2379
+ { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 3" }
1933
2380
  ],
1934
2381
  subtitle: "Trusted by teams and homeowners across Central Texas.",
1935
2382
  title: "Trusted by Local Organizations"
@@ -1970,8 +2417,11 @@ var defaultNodeData = {
1970
2417
  },
1971
2418
  testimonials: {
1972
2419
  ...withSectionStyleDefaults({}),
2420
+ autoRotate: true,
1973
2421
  items: [{ location: "City, ST", name: "Customer Name", quote: "Customer feedback goes here." }],
1974
- title: "What Customers Say"
2422
+ rotateIntervalSeconds: 7,
2423
+ title: "What Customers Say",
2424
+ visibleCount: 3
1975
2425
  },
1976
2426
  stats: withSectionStyleDefaults({
1977
2427
  items: [
@@ -2128,6 +2578,224 @@ var createDefaultStudioDocument = (title) => ({
2128
2578
  nodes: [],
2129
2579
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2130
2580
  });
2581
+
2582
+ // src/nextjs/queries/pages.ts
2583
+ var PAGE_QUERY_CACHE_VERSION = "v2-studio-layout-published-only";
2584
+ function withStudioDocumentLayout(page) {
2585
+ if (!page) {
2586
+ return null;
2587
+ }
2588
+ try {
2589
+ const studioDocument = assertStudioDocumentV1(page.studioDocument);
2590
+ const compiledLayout = studioDocumentToLayout(studioDocument);
2591
+ if (Array.isArray(compiledLayout) && compiledLayout.length > 0) {
2592
+ return {
2593
+ ...page,
2594
+ layout: compiledLayout
2595
+ };
2596
+ }
2597
+ } catch {
2598
+ }
2599
+ return page;
2600
+ }
2601
+ function normalizePath(segments) {
2602
+ if (!segments || segments.length === 0) {
2603
+ return "/";
2604
+ }
2605
+ const cleaned = segments.map((segment) => segment.trim()).filter(Boolean).join("/");
2606
+ return cleaned.length > 0 ? `/${cleaned}` : "/";
2607
+ }
2608
+ async function queryPageByPath(payload, path2, draft) {
2609
+ const pathWhere = {
2610
+ path: {
2611
+ equals: path2
2612
+ }
2613
+ };
2614
+ const publishedWhere = {
2615
+ _status: {
2616
+ equals: "published"
2617
+ }
2618
+ };
2619
+ const result = await payload.find({
2620
+ collection: "pages",
2621
+ depth: 2,
2622
+ draft,
2623
+ limit: 1,
2624
+ overrideAccess: false,
2625
+ where: draft ? pathWhere : { and: [pathWhere, publishedWhere] }
2626
+ });
2627
+ if (result.docs.length > 0) {
2628
+ return withStudioDocumentLayout(result.docs[0] || null);
2629
+ }
2630
+ if (path2 === "/") {
2631
+ const homeWhere = {
2632
+ slug: {
2633
+ equals: "home"
2634
+ }
2635
+ };
2636
+ const homeResult = await payload.find({
2637
+ collection: "pages",
2638
+ depth: 2,
2639
+ draft,
2640
+ limit: 1,
2641
+ overrideAccess: false,
2642
+ where: draft ? homeWhere : { and: [homeWhere, publishedWhere] }
2643
+ });
2644
+ return withStudioDocumentLayout(homeResult.docs[0] || null);
2645
+ }
2646
+ return null;
2647
+ }
2648
+ function createPageQueries(getPayloadClient, contentTag = "website-content") {
2649
+ const getPublishedPageByPathCached = (0, import_cache.unstable_cache)(
2650
+ async (path2) => {
2651
+ const payload = await getPayloadClient();
2652
+ return queryPageByPath(payload, path2, false);
2653
+ },
2654
+ ["page-by-path", PAGE_QUERY_CACHE_VERSION],
2655
+ { tags: [contentTag] }
2656
+ );
2657
+ async function getPageBySegments(segments, draft = false) {
2658
+ const path2 = normalizePath(segments);
2659
+ const payload = await getPayloadClient();
2660
+ if (draft) {
2661
+ return queryPageByPath(payload, path2, true);
2662
+ }
2663
+ return getPublishedPageByPathCached(path2);
2664
+ }
2665
+ async function listPublishedPagePaths() {
2666
+ const payload = await getPayloadClient();
2667
+ const pages = await payload.find({
2668
+ collection: "pages",
2669
+ depth: 0,
2670
+ draft: false,
2671
+ limit: 1e3,
2672
+ pagination: false,
2673
+ overrideAccess: false,
2674
+ where: {
2675
+ _status: {
2676
+ equals: "published"
2677
+ }
2678
+ }
2679
+ });
2680
+ return pages.docs.map((doc) => doc.path).filter((path2) => typeof path2 === "string" && path2.length > 0);
2681
+ }
2682
+ function pathToSegments(path2) {
2683
+ if (!path2 || path2 === "/") {
2684
+ return [];
2685
+ }
2686
+ return path2.split("/").filter(Boolean);
2687
+ }
2688
+ return {
2689
+ getPageBySegments,
2690
+ listPublishedPagePaths,
2691
+ pathToSegments
2692
+ };
2693
+ }
2694
+
2695
+ // src/nextjs/queries/site.ts
2696
+ var import_cache2 = require("next/cache");
2697
+ function createSiteQueries(getPayloadClient, contentTag = "website-content") {
2698
+ const getSiteSettingsCached = (0, import_cache2.unstable_cache)(
2699
+ async () => {
2700
+ const payload = await getPayloadClient();
2701
+ const settings = await payload.findGlobal({
2702
+ slug: "site-settings",
2703
+ depth: 1
2704
+ });
2705
+ return settings;
2706
+ },
2707
+ ["site-settings-global"],
2708
+ { tags: [contentTag] }
2709
+ );
2710
+ const getHeaderCached = (0, import_cache2.unstable_cache)(
2711
+ async () => {
2712
+ const payload = await getPayloadClient();
2713
+ const header = await payload.findGlobal({
2714
+ slug: "header",
2715
+ depth: 1
2716
+ });
2717
+ return header;
2718
+ },
2719
+ ["header-global"],
2720
+ { tags: [contentTag] }
2721
+ );
2722
+ const getFooterCached = (0, import_cache2.unstable_cache)(
2723
+ async () => {
2724
+ const payload = await getPayloadClient();
2725
+ const footer = await payload.findGlobal({
2726
+ slug: "footer",
2727
+ depth: 1
2728
+ });
2729
+ return footer;
2730
+ },
2731
+ ["footer-global"],
2732
+ { tags: [contentTag] }
2733
+ );
2734
+ async function getSiteSettings(draft = false) {
2735
+ if (draft) {
2736
+ const payload = await getPayloadClient();
2737
+ const settings = await payload.findGlobal({
2738
+ slug: "site-settings",
2739
+ depth: 1,
2740
+ draft: true
2741
+ });
2742
+ return settings;
2743
+ }
2744
+ return getSiteSettingsCached();
2745
+ }
2746
+ async function getHeader(draft = false) {
2747
+ if (draft) {
2748
+ const payload = await getPayloadClient();
2749
+ const header = await payload.findGlobal({
2750
+ slug: "header",
2751
+ depth: 1,
2752
+ draft: true
2753
+ });
2754
+ return header;
2755
+ }
2756
+ return getHeaderCached();
2757
+ }
2758
+ async function getFooter(draft = false) {
2759
+ if (draft) {
2760
+ const payload = await getPayloadClient();
2761
+ const footer = await payload.findGlobal({
2762
+ slug: "footer",
2763
+ depth: 1,
2764
+ draft: true
2765
+ });
2766
+ return footer;
2767
+ }
2768
+ return getFooterCached();
2769
+ }
2770
+ return {
2771
+ getSiteSettings,
2772
+ getHeader,
2773
+ getFooter
2774
+ };
2775
+ }
2776
+
2777
+ // src/nextjs/utilities/media.ts
2778
+ function resolveMedia(media) {
2779
+ if (!media) {
2780
+ return null;
2781
+ }
2782
+ if (typeof media === "number" || typeof media === "string") {
2783
+ return null;
2784
+ }
2785
+ if (media.url) {
2786
+ return {
2787
+ url: media.url,
2788
+ alt: media.alt || ""
2789
+ };
2790
+ }
2791
+ if (media.filename) {
2792
+ return {
2793
+ url: `/media/${media.filename}`,
2794
+ alt: media.alt || ""
2795
+ };
2796
+ }
2797
+ return null;
2798
+ }
2131
2799
  // Annotate the CommonJS export names for ESM import in node:
2132
2800
  0 && (module.exports = {
2133
2801
  admin,