@orion-studios/payload-studio 0.6.0-beta.9 → 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 +190 -36
  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-WLOPFFN2.mjs → chunk-MMJNHBOF.mjs} +193 -39
  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-Bm2SaC3r.d.mts → index-BbTcimgH.d.mts} +4 -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-CkT_eyhK.d.ts → index-DV-nW43e.d.ts} +4 -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 +522 -171
  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
package/dist/index.js CHANGED
@@ -107,7 +107,7 @@ var navItemIsActive = (pathname, item) => {
107
107
  };
108
108
 
109
109
  // src/shared/studioSections.ts
110
- var studioRoles = /* @__PURE__ */ new Set(["admin", "editor", "client"]);
110
+ var studioRoles = /* @__PURE__ */ new Set(["admin", "developer", "editor", "client"]);
111
111
  var studioIcons = new Set(adminNavIcons);
112
112
  var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
113
113
  var isAbsoluteExternalURL = (value) => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(value) || value.startsWith("//");
@@ -241,6 +241,7 @@ function configureAdmin(config) {
241
241
  brandPrimary = "#3b82f6",
242
242
  brandSecondary = "#8b5cf6",
243
243
  defaultTheme = "brand-light",
244
+ logoOnDarkUrl,
244
245
  logoUrl,
245
246
  allowThemePreference = false,
246
247
  userSessionDurationSeconds = 60 * 60 * 24
@@ -285,23 +286,33 @@ function configureAdmin(config) {
285
286
  if (cssSources.length === 0) {
286
287
  cssPath = sourceCssPath;
287
288
  } else {
288
- let css = cssSources.map((filePath) => import_fs.default.readFileSync(filePath, "utf-8")).join("\n\n");
289
- css = css.replace(
290
- "--orion-cms-brand-primary-fallback: #3b82f6;",
291
- `--orion-cms-brand-primary-fallback: ${brandPrimary};`
292
- );
293
- css = css.replace(
294
- "--orion-cms-brand-secondary-fallback: #8b5cf6;",
295
- `--orion-cms-brand-secondary-fallback: ${brandSecondary};`
296
- );
297
- const outputBasePath = config.basePath || process.cwd();
298
- const genDir = import_path.default.resolve(outputBasePath, ".generated");
299
- if (!import_fs.default.existsSync(genDir)) {
300
- import_fs.default.mkdirSync(genDir, { recursive: true });
289
+ try {
290
+ let css = cssSources.map((filePath) => import_fs.default.readFileSync(filePath, "utf-8")).join("\n\n");
291
+ css = css.replace(
292
+ "--orion-cms-brand-primary-fallback: #3b82f6;",
293
+ `--orion-cms-brand-primary-fallback: ${brandPrimary};`
294
+ );
295
+ css = css.replace(
296
+ "--orion-cms-brand-secondary-fallback: #8b5cf6;",
297
+ `--orion-cms-brand-secondary-fallback: ${brandSecondary};`
298
+ );
299
+ const outputBasePath = config.basePath || process.cwd();
300
+ const genDir = import_path.default.resolve(outputBasePath, ".generated");
301
+ const genPath = import_path.default.resolve(genDir, "admin.css");
302
+ let existing = null;
303
+ try {
304
+ existing = import_fs.default.readFileSync(genPath, "utf-8");
305
+ } catch {
306
+ existing = null;
307
+ }
308
+ if (existing !== css) {
309
+ import_fs.default.mkdirSync(genDir, { recursive: true });
310
+ import_fs.default.writeFileSync(genPath, css);
311
+ }
312
+ cssPath = genPath;
313
+ } catch {
314
+ cssPath = sourceCssPath;
301
315
  }
302
- const genPath = import_path.default.resolve(genDir, "admin.css");
303
- import_fs.default.writeFileSync(genPath, css);
304
- cssPath = genPath;
305
316
  }
306
317
  const clientPath = "@orion-studios/payload-studio/admin/client";
307
318
  const studioNavClientProps = {
@@ -348,6 +359,49 @@ function configureAdmin(config) {
348
359
  }
349
360
  };
350
361
  };
362
+ const attachStudioEditRedirectToCollection = (collection, options) => {
363
+ if (!studioEnabled) {
364
+ return collection;
365
+ }
366
+ const collectionWithBreadcrumb = attachStudioBackBreadcrumbToCollection(collection);
367
+ const existingViews = collectionWithBreadcrumb.admin?.components?.views;
368
+ const existingEditViews = existingViews?.edit;
369
+ const hasCustomEditView = Boolean(
370
+ existingEditViews?.root || existingEditViews?.default && typeof existingEditViews.default === "object" && existingEditViews.default.Component
371
+ );
372
+ if (hasCustomEditView) {
373
+ return collectionWithBreadcrumb;
374
+ }
375
+ return {
376
+ ...collectionWithBreadcrumb,
377
+ admin: {
378
+ ...collectionWithBreadcrumb.admin,
379
+ components: {
380
+ ...collectionWithBreadcrumb.admin?.components,
381
+ views: {
382
+ ...existingViews,
383
+ edit: {
384
+ ...existingEditViews,
385
+ default: {
386
+ ...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
387
+ Component: {
388
+ exportName: "StudioDocumentRedirect",
389
+ path: clientPath,
390
+ clientProps: {
391
+ description: options.description,
392
+ ...options.emptyHref ? { emptyHref: options.emptyHref } : {},
393
+ ...options.emptyLabel ? { emptyLabel: options.emptyLabel } : {},
394
+ pathBase: options.pathBase,
395
+ title: options.title
396
+ }
397
+ }
398
+ }
399
+ }
400
+ }
401
+ }
402
+ }
403
+ };
404
+ };
351
405
  return {
352
406
  admin: {
353
407
  css: cssPath,
@@ -365,6 +419,7 @@ function configureAdmin(config) {
365
419
  path: clientPath,
366
420
  clientProps: {
367
421
  brandName,
422
+ logoOnDarkUrl,
368
423
  logoUrl
369
424
  }
370
425
  },
@@ -373,6 +428,7 @@ function configureAdmin(config) {
373
428
  path: clientPath,
374
429
  clientProps: {
375
430
  brandName,
431
+ logoOnDarkUrl,
376
432
  logoUrl
377
433
  }
378
434
  }
@@ -387,6 +443,7 @@ function configureAdmin(config) {
387
443
  },
388
444
  ...studioEnabled ? {
389
445
  studioGlobals: {
446
+ exact: true,
390
447
  path: globalsBasePath,
391
448
  Component: {
392
449
  exportName: "AdminStudioGlobalsView",
@@ -398,10 +455,11 @@ function configureAdmin(config) {
398
455
  }
399
456
  }
400
457
  },
401
- studioPages: {
402
- path: pagesBasePath,
458
+ studioPageNew: {
459
+ exact: true,
460
+ path: `${pagesBasePath}/new`,
403
461
  Component: {
404
- exportName: "AdminStudioPagesListView",
462
+ exportName: "AdminStudioNewPageView",
405
463
  path: clientPath,
406
464
  clientProps: {
407
465
  ...studioNavClientProps,
@@ -410,6 +468,7 @@ function configureAdmin(config) {
410
468
  }
411
469
  },
412
470
  studioPageEditor: {
471
+ exact: true,
413
472
  path: `${pagesBasePath}/:id`,
414
473
  Component: {
415
474
  exportName: "AdminStudioPageEditView",
@@ -420,10 +479,11 @@ function configureAdmin(config) {
420
479
  }
421
480
  }
422
481
  },
423
- studioPageNew: {
424
- path: `${pagesBasePath}/new`,
482
+ studioPages: {
483
+ exact: true,
484
+ path: pagesBasePath,
425
485
  Component: {
426
- exportName: "AdminStudioNewPageView",
486
+ exportName: "AdminStudioPagesListView",
427
487
  path: clientPath,
428
488
  clientProps: {
429
489
  ...studioNavClientProps,
@@ -432,6 +492,7 @@ function configureAdmin(config) {
432
492
  }
433
493
  },
434
494
  studioContactForm: {
495
+ exact: true,
435
496
  path: contactFormStudioPath,
436
497
  Component: {
437
498
  exportName: "AdminStudioContactFormView",
@@ -444,7 +505,47 @@ function configureAdmin(config) {
444
505
  }
445
506
  },
446
507
  ...formsEnabled ? {
508
+ studioFormSubmission: {
509
+ exact: true,
510
+ path: `${formsBasePath}/submissions/:id`,
511
+ Component: {
512
+ exportName: "AdminStudioFormSubmissionView",
513
+ path: clientPath,
514
+ clientProps: {
515
+ ...studioNavClientProps,
516
+ formsCollectionSlug,
517
+ formSubmissionsCollectionSlug,
518
+ formUploadsCollectionSlug
519
+ }
520
+ }
521
+ },
522
+ studioFormUpload: {
523
+ exact: true,
524
+ path: `${formsBasePath}/uploads/:id`,
525
+ Component: {
526
+ exportName: "AdminStudioFormUploadView",
527
+ path: clientPath,
528
+ clientProps: {
529
+ ...studioNavClientProps,
530
+ formUploadsCollectionSlug
531
+ }
532
+ }
533
+ },
534
+ studioFormDetail: {
535
+ exact: true,
536
+ path: `${formsBasePath}/:id`,
537
+ Component: {
538
+ exportName: "AdminStudioFormDetailView",
539
+ path: clientPath,
540
+ clientProps: {
541
+ ...studioNavClientProps,
542
+ formsCollectionSlug,
543
+ formSubmissionsCollectionSlug
544
+ }
545
+ }
546
+ },
447
547
  studioForms: {
548
+ exact: true,
448
549
  path: formsBasePath,
449
550
  Component: {
450
551
  exportName: "AdminStudioFormsView",
@@ -458,10 +559,11 @@ function configureAdmin(config) {
458
559
  }
459
560
  }
460
561
  } : {},
461
- studioMedia: {
462
- path: mediaBasePath,
562
+ studioMediaItem: {
563
+ exact: true,
564
+ path: `${mediaBasePath}/:id`,
463
565
  Component: {
464
- exportName: "AdminStudioMediaView",
566
+ exportName: "AdminStudioMediaItemView",
465
567
  path: clientPath,
466
568
  clientProps: {
467
569
  ...studioNavClientProps,
@@ -469,10 +571,11 @@ function configureAdmin(config) {
469
571
  }
470
572
  }
471
573
  },
472
- studioMediaItem: {
473
- path: `${mediaBasePath}/:id`,
574
+ studioMedia: {
575
+ exact: true,
576
+ path: mediaBasePath,
474
577
  Component: {
475
- exportName: "AdminStudioMediaItemView",
578
+ exportName: "AdminStudioMediaView",
476
579
  path: clientPath,
477
580
  clientProps: {
478
581
  ...studioNavClientProps,
@@ -481,6 +584,7 @@ function configureAdmin(config) {
481
584
  }
482
585
  },
483
586
  studioTools: {
587
+ exact: true,
484
588
  path: toolsBasePath,
485
589
  Component: {
486
590
  exportName: "AdminStudioToolsView",
@@ -525,6 +629,7 @@ function configureAdmin(config) {
525
629
  path: clientPath,
526
630
  clientProps: {
527
631
  brandName,
632
+ logoOnDarkUrl,
528
633
  logoUrl
529
634
  }
530
635
  }
@@ -633,25 +738,74 @@ function configureAdmin(config) {
633
738
  return attachStudioBackBreadcrumbToCollection(mediaCollection);
634
739
  },
635
740
  wrapFormsCollection(formsCollection) {
636
- return attachStudioBackBreadcrumbToCollection(formsCollection);
741
+ return attachStudioEditRedirectToCollection(formsCollection, {
742
+ description: "Redirecting to the Studio form workspace.",
743
+ emptyHref: formsBasePath,
744
+ emptyLabel: "Open Forms",
745
+ pathBase: formsBasePath,
746
+ title: "Opening Form..."
747
+ });
637
748
  },
638
749
  wrapFormSubmissionsCollection(formSubmissionsCollection) {
639
- return attachStudioBackBreadcrumbToCollection(formSubmissionsCollection);
750
+ return attachStudioEditRedirectToCollection(formSubmissionsCollection, {
751
+ description: "Redirecting to the Studio submission workspace.",
752
+ emptyHref: formsBasePath,
753
+ emptyLabel: "Open Forms",
754
+ pathBase: `${formsBasePath}/submissions`,
755
+ title: "Opening Submission..."
756
+ });
640
757
  },
641
758
  wrapFormUploadsCollection(formUploadsCollection) {
642
- return attachStudioBackBreadcrumbToCollection(formUploadsCollection);
759
+ return attachStudioEditRedirectToCollection(formUploadsCollection, {
760
+ description: "Redirecting to the Studio upload workspace.",
761
+ emptyHref: formsBasePath,
762
+ emptyLabel: "Open Forms",
763
+ pathBase: `${formsBasePath}/uploads`,
764
+ title: "Opening Upload..."
765
+ });
643
766
  },
644
767
  wrapGlobals(globals2) {
645
- const labelMap = {
768
+ const defaultLabelMap = {
646
769
  header: { group: "Site Design", label: "Header & Navigation" },
647
770
  footer: { group: "Site Design", label: "Footer" },
648
771
  "site-settings": { group: "Site Design", label: "Website Settings" },
649
772
  "social-media": { group: "Site Design", label: "Social Media" },
650
773
  "contact-form": { group: "Lead Forms", label: "Contact Form" }
651
774
  };
775
+ const configuredBySlug = new Map(
776
+ (config.studio?.globals || []).map((link) => [link.slug, link])
777
+ );
778
+ const attachBreadcrumbToGlobal = (global) => {
779
+ if (!studioEnabled) {
780
+ return global;
781
+ }
782
+ const existingControls = global.admin?.components?.elements?.beforeDocumentControls;
783
+ return {
784
+ ...global,
785
+ admin: {
786
+ ...global.admin,
787
+ components: {
788
+ ...global.admin?.components,
789
+ elements: {
790
+ ...global.admin?.components?.elements,
791
+ beforeDocumentControls: appendComponent(
792
+ existingControls,
793
+ studioBackBreadcrumbComponent,
794
+ "StudioBackBreadcrumb"
795
+ )
796
+ }
797
+ }
798
+ }
799
+ };
800
+ };
652
801
  return globals2.map((global) => {
653
- const mapping = labelMap[global.slug];
654
- if (!mapping) return global;
802
+ const configured = configuredBySlug.get(global.slug);
803
+ const defaults = defaultLabelMap[global.slug];
804
+ const mapping = configured || defaults ? {
805
+ group: configured?.group || defaults?.group,
806
+ label: configured?.label || defaults?.label || global.slug
807
+ } : void 0;
808
+ if (!mapping) return attachBreadcrumbToGlobal(global);
655
809
  const shouldAttachSiteSettingsEditView = studioEnabled && global.slug === "site-settings";
656
810
  const shouldAttachSocialMediaEditView = studioEnabled && global.slug === "social-media";
657
811
  const shouldAttachContactFormRedirect = studioEnabled && global.slug === "contact-form";
@@ -760,7 +914,7 @@ function configureAdmin(config) {
760
914
  ...global,
761
915
  admin: {
762
916
  ...global.admin,
763
- group: mapping.group,
917
+ ...mapping.group ? { group: mapping.group } : {},
764
918
  components: {
765
919
  ...global.admin?.components,
766
920
  elements: {
@@ -3217,12 +3371,15 @@ __export(nextjs_exports, {
3217
3371
 
3218
3372
  // src/nextjs/client/payload.ts
3219
3373
  var import_payload = require("payload");
3220
- var payloadPromise = null;
3221
3374
  var WEBSITE_CONTENT_TAG = "website-content";
3222
3375
  function createPayloadClient(config) {
3376
+ let payloadPromise = null;
3223
3377
  return function getPayloadClient() {
3224
3378
  if (!payloadPromise) {
3225
- payloadPromise = (0, import_payload.getPayload)({ config });
3379
+ payloadPromise = Promise.resolve(config).then((resolvedConfig) => (0, import_payload.getPayload)({ config: resolvedConfig })).catch((error) => {
3380
+ payloadPromise = null;
3381
+ throw error;
3382
+ });
3226
3383
  }
3227
3384
  return payloadPromise;
3228
3385
  };
@@ -3580,7 +3737,9 @@ var defaultBuilderBlockSettingsV2 = {
3580
3737
  },
3581
3738
  typography: {
3582
3739
  bodyAlign: "left",
3740
+ bodySizePt: null,
3583
3741
  headingAlign: "left",
3742
+ headingSizePt: null,
3584
3743
  letterSpacingPreset: "normal",
3585
3744
  lineHeightPreset: "normal",
3586
3745
  maxTextWidth: "auto"
@@ -3609,7 +3768,9 @@ var defaultBuilderItemSettingsV2 = {
3609
3768
  },
3610
3769
  typography: {
3611
3770
  bodyAlign: "left",
3771
+ bodySizePt: null,
3612
3772
  headingAlign: "left",
3773
+ headingSizePt: null,
3613
3774
  letterSpacingPreset: "normal",
3614
3775
  lineHeightPreset: "normal",
3615
3776
  maxTextWidth: "auto"
@@ -3639,6 +3800,7 @@ var defaultBuilderThemeTokens = {
3639
3800
 
3640
3801
  // src/studio-pages/builder/adapters/settingsV2.ts
3641
3802
  var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3803
+ var isTextAlign = (value) => value === "left" || value === "center" || value === "right" || value === "justify";
3642
3804
  var parsePercent = (value) => {
3643
3805
  if (typeof value === "number" && Number.isFinite(value)) {
3644
3806
  return Math.max(0, Math.min(100, value));
@@ -3663,6 +3825,18 @@ var parsePixel = (value) => {
3663
3825
  }
3664
3826
  return null;
3665
3827
  };
3828
+ var parsePoint = (value) => {
3829
+ if (typeof value === "number" && Number.isFinite(value)) {
3830
+ return Math.max(6, Math.min(120, Math.round(value)));
3831
+ }
3832
+ if (typeof value === "string" && value.trim().length > 0) {
3833
+ const parsed = Number(value);
3834
+ if (Number.isFinite(parsed)) {
3835
+ return Math.max(6, Math.min(120, Math.round(parsed)));
3836
+ }
3837
+ }
3838
+ return null;
3839
+ };
3666
3840
  var mergeSettings = (defaults, input) => {
3667
3841
  if (!isRecord3(input)) {
3668
3842
  return structuredClone(defaults);
@@ -3681,6 +3855,10 @@ var mergeSettings = (defaults, input) => {
3681
3855
  };
3682
3856
  var legacyBlockToV2Settings = (block) => {
3683
3857
  const current = structuredClone(defaultBuilderBlockSettingsV2);
3858
+ if (block.blockType === "hero" && block.variant === "centered") {
3859
+ current.typography.headingAlign = "center";
3860
+ current.typography.bodyAlign = "center";
3861
+ }
3684
3862
  current.layout.contentWidth = block.contentWidth === "narrow" || block.contentWidth === "content" || block.contentWidth === "wide" || block.contentWidth === "full" || block.contentWidth === "inherit" ? block.contentWidth : current.layout.contentWidth;
3685
3863
  current.layout.sectionPaddingX = block.sectionPaddingX === "none" || block.sectionPaddingX === "sm" || block.sectionPaddingX === "md" || block.sectionPaddingX === "lg" || block.sectionPaddingX === "inherit" ? block.sectionPaddingX : current.layout.sectionPaddingX;
3686
3864
  current.layout.sectionPaddingY = block.sectionPaddingY === "none" || block.sectionPaddingY === "sm" || block.sectionPaddingY === "lg" ? block.sectionPaddingY : current.layout.sectionPaddingY;
@@ -3717,15 +3895,28 @@ var legacyBlockToV2Settings = (block) => {
3717
3895
  current.media.positionX = parsePercent(block.imagePositionX ?? block.backgroundImagePositionX);
3718
3896
  current.media.positionY = parsePercent(block.imagePositionY ?? block.backgroundImagePositionY);
3719
3897
  current.media.height = parsePixel(block.imageHeight);
3720
- current.typography.headingAlign = block.textHeadingAlign === "left" || block.textHeadingAlign === "center" || block.textHeadingAlign === "right" || block.textHeadingAlign === "justify" ? block.textHeadingAlign : current.typography.headingAlign;
3721
- current.typography.bodyAlign = block.textBodyAlign === "left" || block.textBodyAlign === "center" || block.textBodyAlign === "right" || block.textBodyAlign === "justify" ? block.textBodyAlign : current.typography.bodyAlign;
3898
+ current.typography.headingAlign = isTextAlign(block.textHeadingAlign) ? block.textHeadingAlign : current.typography.headingAlign;
3899
+ current.typography.bodyAlign = isTextAlign(block.textBodyAlign) ? block.textBodyAlign : current.typography.bodyAlign;
3900
+ current.typography.headingSizePt = parsePoint(block.textHeadingSizePt);
3901
+ current.typography.bodySizePt = parsePoint(block.textBodySizePt);
3722
3902
  current.typography.maxTextWidth = block.textMaxWidth === "auto" || block.textMaxWidth === "sm" || block.textMaxWidth === "md" || block.textMaxWidth === "lg" || block.textMaxWidth === "full" ? block.textMaxWidth : current.typography.maxTextWidth;
3723
3903
  current.typography.lineHeightPreset = block.textLineHeightPreset === "tight" || block.textLineHeightPreset === "normal" || block.textLineHeightPreset === "relaxed" ? block.textLineHeightPreset : current.typography.lineHeightPreset;
3724
3904
  current.typography.letterSpacingPreset = block.textLetterSpacingPreset === "tight" || block.textLetterSpacingPreset === "normal" || block.textLetterSpacingPreset === "relaxed" ? block.textLetterSpacingPreset : current.typography.letterSpacingPreset;
3725
3905
  current.advanced.editCopyInPanel = Boolean(block.editCopyInPanel ?? current.advanced.editCopyInPanel);
3726
3906
  current.advanced.customClassName = typeof block.customClassName === "string" ? block.customClassName : current.advanced.customClassName;
3727
3907
  current.advanced.hideOnMobile = Boolean(block.hideOnMobile ?? current.advanced.hideOnMobile);
3728
- return mergeSettings(current, block.settings);
3908
+ const settings = mergeSettings(current, block.settings);
3909
+ if (block.blockType === "hero") {
3910
+ const top = settings.layout.paddingTopPt;
3911
+ const bottom = settings.layout.paddingBottomPt;
3912
+ const hasLegacyLinkedHeroPadding = settings.layout.linkVerticalPadding !== false && (top === null || typeof top === "undefined" || top === 30) && (bottom === null || typeof bottom === "undefined" || bottom === 30 || bottom === 20);
3913
+ if (hasLegacyLinkedHeroPadding) {
3914
+ settings.layout.linkVerticalPadding = false;
3915
+ settings.layout.paddingTopPt = 30;
3916
+ settings.layout.paddingBottomPt = 20;
3917
+ }
3918
+ }
3919
+ return settings;
3729
3920
  };
3730
3921
  var v2SettingsToLegacyBlock = (blockWithSettings) => {
3731
3922
  const settings = legacyBlockToV2Settings(blockWithSettings);
@@ -3751,6 +3942,8 @@ var v2SettingsToLegacyBlock = (blockWithSettings) => {
3751
3942
  next.contentGradientAngle = settings.appearance.contentGradientAngle;
3752
3943
  next.textHeadingAlign = settings.typography.headingAlign;
3753
3944
  next.textBodyAlign = settings.typography.bodyAlign;
3945
+ next.textHeadingSizePt = settings.typography.headingSizePt;
3946
+ next.textBodySizePt = settings.typography.bodySizePt;
3754
3947
  next.textMaxWidth = settings.typography.maxTextWidth;
3755
3948
  next.textLineHeightPreset = settings.typography.lineHeightPreset;
3756
3949
  next.textLetterSpacingPreset = settings.typography.letterSpacingPreset;
@@ -3883,7 +4076,7 @@ function normalizePath(segments) {
3883
4076
  const cleaned = segments.map((segment) => segment.trim()).filter(Boolean).join("/");
3884
4077
  return cleaned.length > 0 ? `/${cleaned}` : "/";
3885
4078
  }
3886
- async function queryPageByPath(payload, path2, draft) {
4079
+ async function queryPageByPath(payload, path2, draft, collectionSlug, homeSlug) {
3887
4080
  const pathWhere = {
3888
4081
  path: {
3889
4082
  equals: path2
@@ -3895,7 +4088,7 @@ async function queryPageByPath(payload, path2, draft) {
3895
4088
  }
3896
4089
  };
3897
4090
  const result = await payload.find({
3898
- collection: "pages",
4091
+ collection: collectionSlug,
3899
4092
  depth: 2,
3900
4093
  draft,
3901
4094
  limit: 1,
@@ -3908,11 +4101,11 @@ async function queryPageByPath(payload, path2, draft) {
3908
4101
  if (path2 === "/") {
3909
4102
  const homeWhere = {
3910
4103
  slug: {
3911
- equals: "home"
4104
+ equals: homeSlug
3912
4105
  }
3913
4106
  };
3914
4107
  const homeResult = await payload.find({
3915
- collection: "pages",
4108
+ collection: collectionSlug,
3916
4109
  depth: 2,
3917
4110
  draft,
3918
4111
  limit: 1,
@@ -3923,27 +4116,31 @@ async function queryPageByPath(payload, path2, draft) {
3923
4116
  }
3924
4117
  return null;
3925
4118
  }
3926
- function createPageQueries(getPayloadClient, contentTag = "website-content") {
4119
+ function createPageQueries(getPayloadClient, contentTagOrOptions = "website-content") {
4120
+ const options = typeof contentTagOrOptions === "string" ? { contentTag: contentTagOrOptions } : contentTagOrOptions;
4121
+ const contentTag = options.contentTag || "website-content";
4122
+ const pagesCollectionSlug = options.pagesCollectionSlug || "pages";
4123
+ const homeSlug = options.homeSlug || "home";
3927
4124
  const getPublishedPageByPathCached = (0, import_cache.unstable_cache)(
3928
4125
  async (path2) => {
3929
4126
  const payload = await getPayloadClient();
3930
- return queryPageByPath(payload, path2, false);
4127
+ return queryPageByPath(payload, path2, false, pagesCollectionSlug, homeSlug);
3931
4128
  },
3932
- ["page-by-path", PAGE_QUERY_CACHE_VERSION],
4129
+ ["page-by-path", PAGE_QUERY_CACHE_VERSION, pagesCollectionSlug, homeSlug, contentTag],
3933
4130
  { tags: [contentTag] }
3934
4131
  );
3935
4132
  async function getPageBySegments(segments, draft = false) {
3936
4133
  const path2 = normalizePath(segments);
3937
4134
  const payload = await getPayloadClient();
3938
4135
  if (draft) {
3939
- return queryPageByPath(payload, path2, true);
4136
+ return queryPageByPath(payload, path2, true, pagesCollectionSlug, homeSlug);
3940
4137
  }
3941
4138
  return getPublishedPageByPathCached(path2);
3942
4139
  }
3943
4140
  async function listPublishedPagePaths() {
3944
4141
  const payload = await getPayloadClient();
3945
4142
  const pages = await payload.find({
3946
- collection: "pages",
4143
+ collection: pagesCollectionSlug,
3947
4144
  depth: 0,
3948
4145
  draft: false,
3949
4146
  limit: 1e3,
@@ -3972,108 +4169,46 @@ function createPageQueries(getPayloadClient, contentTag = "website-content") {
3972
4169
 
3973
4170
  // src/nextjs/queries/site.ts
3974
4171
  var import_cache2 = require("next/cache");
3975
- function createSiteQueries(getPayloadClient, contentTag = "website-content") {
3976
- const getSiteSettingsCached = (0, import_cache2.unstable_cache)(
3977
- async () => {
3978
- const payload = await getPayloadClient();
3979
- const settings = await payload.findGlobal({
3980
- slug: "site-settings",
3981
- depth: 1
3982
- });
3983
- return settings;
3984
- },
3985
- ["site-settings-global"],
3986
- { tags: [contentTag] }
3987
- );
3988
- const getHeaderCached = (0, import_cache2.unstable_cache)(
3989
- async () => {
3990
- const payload = await getPayloadClient();
3991
- const header = await payload.findGlobal({
3992
- slug: "header",
3993
- depth: 1
3994
- });
3995
- return header;
3996
- },
3997
- ["header-global"],
3998
- { tags: [contentTag] }
3999
- );
4000
- const getFooterCached = (0, import_cache2.unstable_cache)(
4001
- async () => {
4002
- const payload = await getPayloadClient();
4003
- const footer = await payload.findGlobal({
4004
- slug: "footer",
4005
- depth: 1
4006
- });
4007
- return footer;
4008
- },
4009
- ["footer-global"],
4010
- { tags: [contentTag] }
4011
- );
4012
- const getSocialMediaCached = (0, import_cache2.unstable_cache)(
4013
- async () => {
4014
- const payload = await getPayloadClient();
4015
- const socialMedia = await payload.findGlobal({
4016
- slug: "social-media",
4017
- depth: 1
4018
- });
4019
- return socialMedia;
4020
- },
4021
- ["social-media-global"],
4022
- { tags: [contentTag] }
4023
- );
4024
- async function getSiteSettings(draft = false) {
4025
- if (draft) {
4026
- const payload = await getPayloadClient();
4027
- const settings = await payload.findGlobal({
4028
- slug: "site-settings",
4029
- depth: 1,
4030
- draft: true
4031
- });
4032
- return settings;
4033
- }
4034
- return getSiteSettingsCached();
4035
- }
4036
- async function getHeader(draft = false) {
4037
- if (draft) {
4038
- const payload = await getPayloadClient();
4039
- const header = await payload.findGlobal({
4040
- slug: "header",
4041
- depth: 1,
4042
- draft: true
4043
- });
4044
- return header;
4045
- }
4046
- return getHeaderCached();
4047
- }
4048
- async function getFooter(draft = false) {
4049
- if (draft) {
4050
- const payload = await getPayloadClient();
4051
- const footer = await payload.findGlobal({
4052
- slug: "footer",
4053
- depth: 1,
4054
- draft: true
4055
- });
4056
- return footer;
4057
- }
4058
- return getFooterCached();
4059
- }
4060
- async function getSocialMedia(draft = false) {
4061
- if (draft) {
4062
- const payload = await getPayloadClient();
4063
- const socialMedia = await payload.findGlobal({
4064
- slug: "social-media",
4065
- depth: 1,
4066
- draft: true
4067
- });
4068
- return socialMedia;
4069
- }
4070
- return getSocialMediaCached();
4172
+ function createSiteQueries(getPayloadClient, contentTagOrOptions = "website-content") {
4173
+ const options = typeof contentTagOrOptions === "string" ? { contentTag: contentTagOrOptions } : contentTagOrOptions;
4174
+ const contentTag = options.contentTag || "website-content";
4175
+ const siteSettingsSlug = options.siteSettingsSlug || "site-settings";
4176
+ const headerSlug = options.headerSlug || "header";
4177
+ const footerSlug = options.footerSlug || "footer";
4178
+ const socialMediaSlug = options.socialMediaSlug || "social-media";
4179
+ function createGlobalQuery(slug, cacheKey) {
4180
+ const getCached = (0, import_cache2.unstable_cache)(
4181
+ async () => {
4182
+ const payload = await getPayloadClient();
4183
+ const data = await payload.findGlobal({
4184
+ slug,
4185
+ depth: 1,
4186
+ overrideAccess: false
4187
+ });
4188
+ return data;
4189
+ },
4190
+ [cacheKey, slug, contentTag],
4191
+ { tags: [contentTag] }
4192
+ );
4193
+ return async function getGlobal(draft = false) {
4194
+ if (draft) {
4195
+ const payload = await getPayloadClient();
4196
+ const data = await payload.findGlobal({
4197
+ slug,
4198
+ depth: 1,
4199
+ draft: true,
4200
+ overrideAccess: false
4201
+ });
4202
+ return data;
4203
+ }
4204
+ return getCached();
4205
+ };
4071
4206
  }
4072
4207
  return {
4073
- getSiteSettings,
4074
- getHeader,
4075
- getFooter,
4076
- getSocialMedia
4208
+ getSiteSettings: createGlobalQuery(siteSettingsSlug, "site-settings-global"),
4209
+ getHeader: createGlobalQuery(headerSlug, "header-global"),
4210
+ getFooter: createGlobalQuery(footerSlug, "footer-global"),
4211
+ getSocialMedia: createGlobalQuery(socialMediaSlug, "social-media-global")
4077
4212
  };
4078
4213
  }
4079
4214
 
@@ -4129,9 +4264,13 @@ function resolveSocialMediaLinks(data) {
4129
4264
  // src/studio-pages/index.ts
4130
4265
  var studio_pages_exports = {};
4131
4266
  __export(studio_pages_exports, {
4267
+ DEFAULT_LEGACY_HERO_BACKGROUND_COLORS: () => DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
4132
4268
  createDefaultStudioDocument: () => createDefaultStudioDocument,
4269
+ createStudioPageFields: () => createStudioPageFields,
4133
4270
  createStudioPageService: () => createStudioPageService,
4271
+ createStudioPagesCollection: () => createStudioPagesCollection,
4134
4272
  defaultBuilderThemeTokens: () => defaultBuilderThemeTokens,
4273
+ defaultRelationshipHydrationMap: () => defaultRelationshipHydrationMap,
4135
4274
  getStudioDocumentFromPage: () => getStudioDocumentFromPage,
4136
4275
  layoutToStudioDocument: () => layoutToStudioDocument,
4137
4276
  pageInspectorPanels: () => pageInspectorPanels,
@@ -4174,8 +4313,52 @@ var alignOptions = [
4174
4313
  { label: "Right", value: "right" },
4175
4314
  { label: "Justify", value: "justify" }
4176
4315
  ];
4177
- var layoutFieldSet = [];
4316
+ var layoutFieldSet = [
4317
+ {
4318
+ group: "layout",
4319
+ key: "settings.layout.linkVerticalPadding",
4320
+ label: "Link Top/Bottom Padding",
4321
+ tags: ["spacing", "padding", "vertical"],
4322
+ type: "checkbox"
4323
+ },
4324
+ {
4325
+ group: "layout",
4326
+ key: "settings.layout.paddingTopPt",
4327
+ label: "Top Padding (pt)",
4328
+ max: 240,
4329
+ min: 0,
4330
+ tags: ["spacing", "padding", "top"],
4331
+ type: "number"
4332
+ },
4333
+ {
4334
+ group: "layout",
4335
+ key: "settings.layout.paddingBottomPt",
4336
+ label: "Bottom Padding (pt)",
4337
+ max: 240,
4338
+ min: 0,
4339
+ tags: ["spacing", "padding", "bottom"],
4340
+ type: "number"
4341
+ }
4342
+ ];
4178
4343
  var typographyFieldSet = [
4344
+ {
4345
+ group: "typography",
4346
+ key: "settings.typography.headingSizePt",
4347
+ label: "Heading Text Size (pt)",
4348
+ max: 120,
4349
+ min: 6,
4350
+ tags: ["text", "size", "heading", "font"],
4351
+ type: "number"
4352
+ },
4353
+ {
4354
+ group: "typography",
4355
+ key: "settings.typography.bodySizePt",
4356
+ label: "Body Text Size (pt)",
4357
+ max: 72,
4358
+ min: 6,
4359
+ tags: ["text", "size", "body", "paragraph", "font"],
4360
+ type: "number"
4361
+ },
4179
4362
  {
4180
4363
  group: "typography",
4181
4364
  key: "settings.typography.headingAlign",
@@ -4392,6 +4575,13 @@ var inspectorDefinitionByBlockType = {
4392
4575
  { group: "basics", inlineEditable: true, key: "kicker", label: "Kicker", type: "text" },
4393
4576
  { group: "basics", inlineEditable: true, key: "headline", label: "Headline", type: "text" },
4394
4577
  { group: "basics", inlineEditable: true, key: "subheadline", label: "Subheadline", type: "textarea" },
4578
+ {
4579
+ group: "basics",
4580
+ key: "settings.marquee.itemsText",
4581
+ label: "Scrolling Bar Options",
4582
+ tags: ["marquee", "scrolling", "bar", "categories"],
4583
+ type: "textarea"
4584
+ },
4395
4585
  {
4396
4586
  group: "basics",
4397
4587
  key: "variant",
@@ -4518,6 +4708,13 @@ var inspectorDefinitionByBlockType = {
4518
4708
  };
4519
4709
 
4520
4710
  // src/studio-pages/pageService.ts
4711
+ var defaultRelationshipHydrationMap = {
4712
+ beforeAfter: { arrays: { items: ["media", "beforeMedia", "afterMedia"] } },
4713
+ featureGrid: { arrays: { items: ["media", "beforeMedia", "afterMedia"] } },
4714
+ hero: { fields: ["media"] },
4715
+ logoWall: { arrays: { items: ["media", "beforeMedia", "afterMedia"] } },
4716
+ media: { fields: ["image"] }
4717
+ };
4521
4718
  var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
4522
4719
  var toRecordArray = (value) => Array.isArray(value) ? value.filter((item) => isRecord5(item)) : [];
4523
4720
  var getRelationID = (value) => {
@@ -4545,7 +4742,7 @@ var hydrateRelationship = (valueFromStudio, valueFromLayout) => {
4545
4742
  }
4546
4743
  return valueFromLayout;
4547
4744
  };
4548
- var hydrateDocumentWithLayoutRelations = (document, layout) => {
4745
+ var hydrateDocumentWithLayoutRelations = (document, layout, hydrationMap = defaultRelationshipHydrationMap) => {
4549
4746
  const nextNodes = document.nodes.map((node, index) => {
4550
4747
  const layoutBlock = layout[index];
4551
4748
  if (!isRecord5(layoutBlock)) {
@@ -4554,17 +4751,18 @@ var hydrateDocumentWithLayoutRelations = (document, layout) => {
4554
4751
  if (typeof layoutBlock.blockType !== "string" || layoutBlock.blockType !== node.type) {
4555
4752
  return node;
4556
4753
  }
4557
- const nextData = { ...node.data };
4558
- if (node.type === "hero") {
4559
- nextData.media = hydrateRelationship(nextData.media, layoutBlock.media);
4754
+ const rule = hydrationMap[node.type];
4755
+ if (!rule) {
4756
+ return node;
4560
4757
  }
4561
- if (node.type === "media") {
4562
- nextData.image = hydrateRelationship(nextData.image, layoutBlock.image);
4758
+ const nextData = { ...node.data };
4759
+ for (const fieldName of rule.fields || []) {
4760
+ nextData[fieldName] = hydrateRelationship(nextData[fieldName], layoutBlock[fieldName]);
4563
4761
  }
4564
- if (node.type === "featureGrid" || node.type === "logoWall" || node.type === "beforeAfter") {
4565
- const studioItems = Array.isArray(nextData.items) ? nextData.items : [];
4566
- const layoutItems = Array.isArray(layoutBlock.items) ? layoutBlock.items : [];
4567
- nextData.items = studioItems.map((rawStudioItem, itemIndex) => {
4762
+ for (const [arrayField, itemFields] of Object.entries(rule.arrays || {})) {
4763
+ const studioItems = Array.isArray(nextData[arrayField]) ? nextData[arrayField] : [];
4764
+ const layoutItems = Array.isArray(layoutBlock[arrayField]) ? layoutBlock[arrayField] : [];
4765
+ nextData[arrayField] = studioItems.map((rawStudioItem, itemIndex) => {
4568
4766
  if (!isRecord5(rawStudioItem)) {
4569
4767
  return rawStudioItem;
4570
4768
  }
@@ -4573,9 +4771,9 @@ var hydrateDocumentWithLayoutRelations = (document, layout) => {
4573
4771
  return rawStudioItem;
4574
4772
  }
4575
4773
  const nextItem = { ...rawStudioItem };
4576
- nextItem.media = hydrateRelationship(nextItem.media, layoutItem.media);
4577
- nextItem.beforeMedia = hydrateRelationship(nextItem.beforeMedia, layoutItem.beforeMedia);
4578
- nextItem.afterMedia = hydrateRelationship(nextItem.afterMedia, layoutItem.afterMedia);
4774
+ for (const itemField of itemFields) {
4775
+ nextItem[itemField] = hydrateRelationship(nextItem[itemField], layoutItem[itemField]);
4776
+ }
4579
4777
  return nextItem;
4580
4778
  });
4581
4779
  }
@@ -4594,7 +4792,12 @@ var normalizeDocument = (document) => ({
4594
4792
  schemaVersion: 1,
4595
4793
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4596
4794
  });
4597
- var normalizeLegacyHeroDefaults = (document) => {
4795
+ var DEFAULT_LEGACY_HERO_BACKGROUND_COLORS = ["#124a37"];
4796
+ var normalizeLegacyHeroDefaults = (document, legacyColors = DEFAULT_LEGACY_HERO_BACKGROUND_COLORS) => {
4797
+ if (legacyColors.length === 0) {
4798
+ return document;
4799
+ }
4800
+ const normalizedLegacyColors = legacyColors.map((color) => color.trim().toLowerCase());
4598
4801
  const nodes = document.nodes.map((node) => {
4599
4802
  if (node.type !== "hero" || !isRecord5(node.data)) {
4600
4803
  return node;
@@ -4604,7 +4807,7 @@ var normalizeLegacyHeroDefaults = (document) => {
4604
4807
  const sectionBackgroundMode = typeof nextData.sectionBackgroundMode === "string" ? nextData.sectionBackgroundMode : "none";
4605
4808
  const hasBackgroundImageURL = typeof nextData.backgroundImageURL === "string" && nextData.backgroundImageURL.trim().length > 0;
4606
4809
  const mediaRelation = getRelationID(nextData.media);
4607
- if (heroBackgroundColor === "#124a37" && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
4810
+ if (normalizedLegacyColors.includes(heroBackgroundColor) && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
4608
4811
  nextData.backgroundColor = "";
4609
4812
  }
4610
4813
  return {
@@ -4623,13 +4826,13 @@ var assertCanPublish = (issues) => {
4623
4826
  throw new Error(`Cannot publish page: ${publishErrors[0].message}`);
4624
4827
  }
4625
4828
  };
4626
- var getStudioDocumentFromPage = (doc) => {
4829
+ var getStudioDocumentFromPage = (doc, relationshipHydration = defaultRelationshipHydrationMap) => {
4627
4830
  const title = typeof doc.title === "string" ? doc.title : void 0;
4628
4831
  const layout = asLayoutArray(doc.layout);
4629
4832
  try {
4630
4833
  const studioDocument = assertStudioDocumentV1(doc.studioDocument);
4631
4834
  if (layout.length > 0) {
4632
- return hydrateDocumentWithLayoutRelations(studioDocument, layout);
4835
+ return hydrateDocumentWithLayoutRelations(studioDocument, layout, relationshipHydration);
4633
4836
  }
4634
4837
  return studioDocument;
4635
4838
  } catch {
@@ -4641,8 +4844,10 @@ var getStudioDocumentFromPage = (doc) => {
4641
4844
  };
4642
4845
  var createStudioPageService = ({
4643
4846
  collectionSlug = "pages",
4847
+ legacyHeroBackgroundColors = DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
4644
4848
  modules,
4645
4849
  payload,
4850
+ relationshipHydration = defaultRelationshipHydrationMap,
4646
4851
  user
4647
4852
  }) => ({
4648
4853
  getPageForStudio: async (pageID) => {
@@ -4654,7 +4859,7 @@ var createStudioPageService = ({
4654
4859
  overrideAccess: false,
4655
4860
  user
4656
4861
  });
4657
- const studioDocument = getStudioDocumentFromPage(page);
4862
+ const studioDocument = getStudioDocumentFromPage(page, relationshipHydration);
4658
4863
  return {
4659
4864
  id: String(page.id),
4660
4865
  slug: typeof page.slug === "string" ? page.slug : "",
@@ -4664,7 +4869,10 @@ var createStudioPageService = ({
4664
4869
  },
4665
4870
  validateStudioDocument: (document) => validateStudioDocument(document, modules),
4666
4871
  saveDraft: async (pageID, document, _metadata) => {
4667
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
4872
+ const normalizedDocument = normalizeLegacyHeroDefaults(
4873
+ normalizeDocument(document),
4874
+ legacyHeroBackgroundColors
4875
+ );
4668
4876
  const compileResult = compileStudioDocument(normalizedDocument, modules);
4669
4877
  await payload.update({
4670
4878
  collection: collectionSlug,
@@ -4685,7 +4893,10 @@ var createStudioPageService = ({
4685
4893
  };
4686
4894
  },
4687
4895
  publish: async (pageID, document, _metadata) => {
4688
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
4896
+ const normalizedDocument = normalizeLegacyHeroDefaults(
4897
+ normalizeDocument(document),
4898
+ legacyHeroBackgroundColors
4899
+ );
4689
4900
  const compileResult = compileStudioDocument(normalizedDocument, modules);
4690
4901
  assertCanPublish(compileResult.issues);
4691
4902
  await payload.update({
@@ -4714,6 +4925,146 @@ var toEditorInitialDoc = (payloadPage) => ({
4714
4925
  title: payloadPage.title
4715
4926
  });
4716
4927
 
4928
+ // src/studio-pages/pageFields.ts
4929
+ var formatSlugValue = (value) => {
4930
+ if (typeof value !== "string") return void 0;
4931
+ return value.trim().toLowerCase().replace(/[^a-z0-9/]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
4932
+ };
4933
+ function createStudioPageFields(options = {}) {
4934
+ const {
4935
+ blocks = defaultPageLayoutBlocks,
4936
+ mediaCollectionSlug = "media",
4937
+ additionalFields = []
4938
+ } = options;
4939
+ return [
4940
+ {
4941
+ name: "title",
4942
+ type: "text",
4943
+ required: true
4944
+ },
4945
+ {
4946
+ name: "slug",
4947
+ type: "text",
4948
+ admin: {
4949
+ description: "URL identifier for this page. `home` is served at `/`.",
4950
+ position: "sidebar"
4951
+ },
4952
+ hooks: {
4953
+ beforeValidate: [({ value }) => formatSlugValue(value) ?? value]
4954
+ },
4955
+ index: true,
4956
+ required: true,
4957
+ unique: true
4958
+ },
4959
+ {
4960
+ name: "path",
4961
+ type: "text",
4962
+ admin: {
4963
+ description: "Full public path. Derived from the slug when left empty.",
4964
+ position: "sidebar"
4965
+ },
4966
+ hooks: {
4967
+ beforeValidate: [
4968
+ ({ value, data }) => {
4969
+ if (typeof value === "string" && value.trim().length > 0) {
4970
+ return value.trim();
4971
+ }
4972
+ const slug = formatSlugValue(data?.slug);
4973
+ if (!slug) return value;
4974
+ return slug === "home" ? "/" : `/${slug}`;
4975
+ }
4976
+ ]
4977
+ },
4978
+ index: true,
4979
+ unique: true
4980
+ },
4981
+ {
4982
+ name: "studioDocument",
4983
+ type: "json",
4984
+ admin: {
4985
+ description: "Canonical Studio editor state. Managed by the page builder.",
4986
+ hidden: true
4987
+ }
4988
+ },
4989
+ {
4990
+ name: "studioValidationIssues",
4991
+ type: "json",
4992
+ admin: {
4993
+ description: "Validation issues from the last Studio compile. Managed by the page builder.",
4994
+ hidden: true
4995
+ }
4996
+ },
4997
+ {
4998
+ name: "layout",
4999
+ type: "blocks",
5000
+ admin: {
5001
+ description: "Compiled output only \u2014 edit pages through the Studio builder."
5002
+ },
5003
+ blocks
5004
+ },
5005
+ {
5006
+ name: "seo",
5007
+ type: "group",
5008
+ fields: [
5009
+ { name: "metaTitle", type: "text" },
5010
+ { name: "metaDescription", type: "textarea" },
5011
+ { name: "canonicalUrl", type: "text" },
5012
+ {
5013
+ name: "ogImage",
5014
+ type: "upload",
5015
+ relationTo: mediaCollectionSlug
5016
+ }
5017
+ ]
5018
+ },
5019
+ {
5020
+ name: "noIndex",
5021
+ type: "checkbox",
5022
+ admin: { position: "sidebar" },
5023
+ defaultValue: false
5024
+ },
5025
+ {
5026
+ name: "noFollow",
5027
+ type: "checkbox",
5028
+ admin: { position: "sidebar" },
5029
+ defaultValue: false
5030
+ },
5031
+ ...additionalFields
5032
+ ];
5033
+ }
5034
+ function createStudioPagesCollection(options = {}) {
5035
+ const { slug = "pages", access, overrides, ...fieldOptions } = options;
5036
+ const base = {
5037
+ slug,
5038
+ access: access || {
5039
+ create: ({ req }) => Boolean(req.user),
5040
+ delete: ({ req }) => Boolean(req.user),
5041
+ read: ({ req }) => {
5042
+ if (req.user) return true;
5043
+ return { _status: { equals: "published" } };
5044
+ },
5045
+ update: ({ req }) => Boolean(req.user)
5046
+ },
5047
+ admin: {
5048
+ defaultColumns: ["title", "slug", "_status", "updatedAt"],
5049
+ useAsTitle: "title"
5050
+ },
5051
+ fields: createStudioPageFields(fieldOptions),
5052
+ versions: {
5053
+ drafts: {
5054
+ autosave: false
5055
+ },
5056
+ maxPerDoc: 50
5057
+ }
5058
+ };
5059
+ if (!overrides) return base;
5060
+ return {
5061
+ ...base,
5062
+ ...overrides,
5063
+ admin: { ...base.admin, ...overrides.admin },
5064
+ fields: overrides.fields || base.fields
5065
+ };
5066
+ }
5067
+
4717
5068
  // src/studio-pages/migrations.ts
4718
5069
  var isRecord6 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
4719
5070
  var assertPageStudioDocumentV1 = (value) => {