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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +6 -1
  2. package/bin/init.js +329 -0
  3. package/dist/admin/client.js +2106 -585
  4. package/dist/admin/client.mjs +2161 -653
  5. package/dist/admin/index.d.mts +2 -2
  6. package/dist/admin/index.d.ts +2 -2
  7. package/dist/admin/index.js +201 -37
  8. package/dist/admin/index.mjs +2 -2
  9. package/dist/admin-app/client.js +14 -5
  10. package/dist/admin-app/client.mjs +1 -1
  11. package/dist/admin-app/index.d.mts +2 -2
  12. package/dist/admin-app/index.d.ts +2 -2
  13. package/dist/admin-app/styles.css +361 -41
  14. package/dist/admin.css +98 -2
  15. package/dist/blocks/index.mjs +1 -2
  16. package/dist/builder-v2/client.d.mts +18 -0
  17. package/dist/builder-v2/client.d.ts +18 -0
  18. package/dist/builder-v2/client.js +4287 -0
  19. package/dist/builder-v2/client.mjs +4162 -0
  20. package/dist/builder-v2/index.d.mts +249 -0
  21. package/dist/builder-v2/index.d.ts +249 -0
  22. package/dist/builder-v2/index.js +836 -0
  23. package/dist/builder-v2/index.mjs +786 -0
  24. package/dist/builder-v2/styles.css +2705 -0
  25. package/dist/{chunk-PF3EBZXF.mjs → chunk-7ZMXZRBP.mjs} +39 -3
  26. package/dist/{chunk-XKUTZ7IU.mjs → chunk-DTHBPJXU.mjs} +246 -25
  27. package/dist/{chunk-JQAHXYAM.mjs → chunk-KMYMSR7W.mjs} +163 -3
  28. package/dist/{chunk-5FNTVRCR.mjs → chunk-MMJNHBOF.mjs} +204 -40
  29. package/dist/{chunk-KPIX7OSV.mjs → chunk-NF37A575.mjs} +14 -5
  30. package/dist/{chunk-OTHERBGX.mjs → chunk-OF6BATTO.mjs} +57 -112
  31. package/dist/chunk-VA545CHJ.mjs +202 -0
  32. package/dist/forms/index.d.mts +25 -0
  33. package/dist/forms/index.d.ts +25 -0
  34. package/dist/forms/index.js +237 -0
  35. package/dist/forms/index.mjs +25 -0
  36. package/dist/forms/server.d.mts +35 -0
  37. package/dist/forms/server.d.ts +35 -0
  38. package/dist/forms/server.js +442 -0
  39. package/dist/forms/server.mjs +228 -0
  40. package/dist/{index-Crx_MtPw.d.ts → index-0xXWuOuz.d.ts} +38 -6
  41. package/dist/{index-Cv-6qnrw.d.mts → index-BU82akSL.d.mts} +38 -6
  42. package/dist/{index-DyMmaRfI.d.mts → index-BbTcimgH.d.mts} +5 -2
  43. package/dist/{index-D8BNfUJb.d.mts → index-Cen-9wcc.d.mts} +17 -5
  44. package/dist/{index-52HdVLQq.d.ts → index-DAdN56fM.d.ts} +1 -1
  45. package/dist/{index-QPDAedIX.d.ts → index-DV-nW43e.d.ts} +5 -2
  46. package/dist/{index-DEQC3Dwj.d.mts → index-G_uTNffQ.d.mts} +1 -1
  47. package/dist/{index-DD_E2UfJ.d.ts → index-gxcMPpm-.d.ts} +17 -5
  48. package/dist/index.d.mts +5 -5
  49. package/dist/index.d.ts +5 -5
  50. package/dist/index.js +533 -172
  51. package/dist/index.mjs +10 -11
  52. package/dist/nextjs/index.d.mts +1 -1
  53. package/dist/nextjs/index.d.ts +1 -1
  54. package/dist/nextjs/index.js +95 -114
  55. package/dist/nextjs/index.mjs +2 -2
  56. package/dist/{sitePreviewTypes-BkHCWxNW.d.mts → sitePreviewTypes-BrJwGzJj.d.mts} +1 -1
  57. package/dist/{sitePreviewTypes-BkHCWxNW.d.ts → sitePreviewTypes-BrJwGzJj.d.ts} +1 -1
  58. package/dist/studio-pages/builder.css +66 -5
  59. package/dist/studio-pages/client.js +618 -73
  60. package/dist/studio-pages/client.mjs +641 -96
  61. package/dist/studio-pages/index.d.mts +1 -1
  62. package/dist/studio-pages/index.d.ts +1 -1
  63. package/dist/studio-pages/index.js +1893 -24
  64. package/dist/studio-pages/index.mjs +11 -3
  65. package/package.json +38 -3
  66. package/dist/chunk-OQSEJXC4.mjs +0 -166
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,8 +241,10 @@ function configureAdmin(config) {
241
241
  brandPrimary = "#3b82f6",
242
242
  brandSecondary = "#8b5cf6",
243
243
  defaultTheme = "brand-light",
244
+ logoOnDarkUrl,
244
245
  logoUrl,
245
- allowThemePreference = false
246
+ allowThemePreference = false,
247
+ userSessionDurationSeconds = 60 * 60 * 24
246
248
  } = config;
247
249
  const studioEnabled = config.studio?.enabled ?? true;
248
250
  const formsEnabled = config.studio?.forms?.enabled ?? false;
@@ -284,23 +286,33 @@ function configureAdmin(config) {
284
286
  if (cssSources.length === 0) {
285
287
  cssPath = sourceCssPath;
286
288
  } else {
287
- let css = cssSources.map((filePath) => import_fs.default.readFileSync(filePath, "utf-8")).join("\n\n");
288
- css = css.replace(
289
- "--orion-cms-brand-primary-fallback: #3b82f6;",
290
- `--orion-cms-brand-primary-fallback: ${brandPrimary};`
291
- );
292
- css = css.replace(
293
- "--orion-cms-brand-secondary-fallback: #8b5cf6;",
294
- `--orion-cms-brand-secondary-fallback: ${brandSecondary};`
295
- );
296
- const outputBasePath = config.basePath || process.cwd();
297
- const genDir = import_path.default.resolve(outputBasePath, ".generated");
298
- if (!import_fs.default.existsSync(genDir)) {
299
- 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;
300
315
  }
301
- const genPath = import_path.default.resolve(genDir, "admin.css");
302
- import_fs.default.writeFileSync(genPath, css);
303
- cssPath = genPath;
304
316
  }
305
317
  const clientPath = "@orion-studios/payload-studio/admin/client";
306
318
  const studioNavClientProps = {
@@ -347,6 +359,49 @@ function configureAdmin(config) {
347
359
  }
348
360
  };
349
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
+ };
350
405
  return {
351
406
  admin: {
352
407
  css: cssPath,
@@ -364,6 +419,7 @@ function configureAdmin(config) {
364
419
  path: clientPath,
365
420
  clientProps: {
366
421
  brandName,
422
+ logoOnDarkUrl,
367
423
  logoUrl
368
424
  }
369
425
  },
@@ -372,6 +428,7 @@ function configureAdmin(config) {
372
428
  path: clientPath,
373
429
  clientProps: {
374
430
  brandName,
431
+ logoOnDarkUrl,
375
432
  logoUrl
376
433
  }
377
434
  }
@@ -386,6 +443,7 @@ function configureAdmin(config) {
386
443
  },
387
444
  ...studioEnabled ? {
388
445
  studioGlobals: {
446
+ exact: true,
389
447
  path: globalsBasePath,
390
448
  Component: {
391
449
  exportName: "AdminStudioGlobalsView",
@@ -397,10 +455,11 @@ function configureAdmin(config) {
397
455
  }
398
456
  }
399
457
  },
400
- studioPages: {
401
- path: pagesBasePath,
458
+ studioPageNew: {
459
+ exact: true,
460
+ path: `${pagesBasePath}/new`,
402
461
  Component: {
403
- exportName: "AdminStudioPagesListView",
462
+ exportName: "AdminStudioNewPageView",
404
463
  path: clientPath,
405
464
  clientProps: {
406
465
  ...studioNavClientProps,
@@ -409,6 +468,7 @@ function configureAdmin(config) {
409
468
  }
410
469
  },
411
470
  studioPageEditor: {
471
+ exact: true,
412
472
  path: `${pagesBasePath}/:id`,
413
473
  Component: {
414
474
  exportName: "AdminStudioPageEditView",
@@ -419,10 +479,11 @@ function configureAdmin(config) {
419
479
  }
420
480
  }
421
481
  },
422
- studioPageNew: {
423
- path: `${pagesBasePath}/new`,
482
+ studioPages: {
483
+ exact: true,
484
+ path: pagesBasePath,
424
485
  Component: {
425
- exportName: "AdminStudioNewPageView",
486
+ exportName: "AdminStudioPagesListView",
426
487
  path: clientPath,
427
488
  clientProps: {
428
489
  ...studioNavClientProps,
@@ -431,6 +492,7 @@ function configureAdmin(config) {
431
492
  }
432
493
  },
433
494
  studioContactForm: {
495
+ exact: true,
434
496
  path: contactFormStudioPath,
435
497
  Component: {
436
498
  exportName: "AdminStudioContactFormView",
@@ -443,7 +505,47 @@ function configureAdmin(config) {
443
505
  }
444
506
  },
445
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
+ },
446
547
  studioForms: {
548
+ exact: true,
447
549
  path: formsBasePath,
448
550
  Component: {
449
551
  exportName: "AdminStudioFormsView",
@@ -457,10 +559,11 @@ function configureAdmin(config) {
457
559
  }
458
560
  }
459
561
  } : {},
460
- studioMedia: {
461
- path: mediaBasePath,
562
+ studioMediaItem: {
563
+ exact: true,
564
+ path: `${mediaBasePath}/:id`,
462
565
  Component: {
463
- exportName: "AdminStudioMediaView",
566
+ exportName: "AdminStudioMediaItemView",
464
567
  path: clientPath,
465
568
  clientProps: {
466
569
  ...studioNavClientProps,
@@ -468,10 +571,11 @@ function configureAdmin(config) {
468
571
  }
469
572
  }
470
573
  },
471
- studioMediaItem: {
472
- path: `${mediaBasePath}/:id`,
574
+ studioMedia: {
575
+ exact: true,
576
+ path: mediaBasePath,
473
577
  Component: {
474
- exportName: "AdminStudioMediaItemView",
578
+ exportName: "AdminStudioMediaView",
475
579
  path: clientPath,
476
580
  clientProps: {
477
581
  ...studioNavClientProps,
@@ -480,6 +584,7 @@ function configureAdmin(config) {
480
584
  }
481
585
  },
482
586
  studioTools: {
587
+ exact: true,
483
588
  path: toolsBasePath,
484
589
  Component: {
485
590
  exportName: "AdminStudioToolsView",
@@ -524,6 +629,7 @@ function configureAdmin(config) {
524
629
  path: clientPath,
525
630
  clientProps: {
526
631
  brandName,
632
+ logoOnDarkUrl,
527
633
  logoUrl
528
634
  }
529
635
  }
@@ -560,8 +666,17 @@ function configureAdmin(config) {
560
666
  const hasThemePreference = existingFields.some(
561
667
  (field) => typeof field === "object" && field !== null && "name" in field && field.name === "themePreference"
562
668
  );
669
+ const normalizedAuth = usersCollection.auth === true ? {
670
+ tokenExpiration: userSessionDurationSeconds,
671
+ useSessions: true
672
+ } : usersCollection.auth && typeof usersCollection.auth === "object" ? {
673
+ ...usersCollection.auth,
674
+ tokenExpiration: usersCollection.auth.tokenExpiration ?? userSessionDurationSeconds,
675
+ useSessions: usersCollection.auth.useSessions ?? true
676
+ } : usersCollection.auth;
563
677
  const nextCollection = {
564
678
  ...usersCollection,
679
+ auth: normalizedAuth,
565
680
  fields: !allowThemePreference || hasThemePreference ? existingFields : [...existingFields, createThemePreferenceField(defaultTheme)]
566
681
  };
567
682
  return attachStudioBackBreadcrumbToCollection(nextCollection);
@@ -623,25 +738,74 @@ function configureAdmin(config) {
623
738
  return attachStudioBackBreadcrumbToCollection(mediaCollection);
624
739
  },
625
740
  wrapFormsCollection(formsCollection) {
626
- 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
+ });
627
748
  },
628
749
  wrapFormSubmissionsCollection(formSubmissionsCollection) {
629
- 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
+ });
630
757
  },
631
758
  wrapFormUploadsCollection(formUploadsCollection) {
632
- 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
+ });
633
766
  },
634
767
  wrapGlobals(globals2) {
635
- const labelMap = {
768
+ const defaultLabelMap = {
636
769
  header: { group: "Site Design", label: "Header & Navigation" },
637
770
  footer: { group: "Site Design", label: "Footer" },
638
771
  "site-settings": { group: "Site Design", label: "Website Settings" },
639
772
  "social-media": { group: "Site Design", label: "Social Media" },
640
773
  "contact-form": { group: "Lead Forms", label: "Contact Form" }
641
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
+ };
642
801
  return globals2.map((global) => {
643
- const mapping = labelMap[global.slug];
644
- 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);
645
809
  const shouldAttachSiteSettingsEditView = studioEnabled && global.slug === "site-settings";
646
810
  const shouldAttachSocialMediaEditView = studioEnabled && global.slug === "social-media";
647
811
  const shouldAttachContactFormRedirect = studioEnabled && global.slug === "contact-form";
@@ -750,7 +914,7 @@ function configureAdmin(config) {
750
914
  ...global,
751
915
  admin: {
752
916
  ...global.admin,
753
- group: mapping.group,
917
+ ...mapping.group ? { group: mapping.group } : {},
754
918
  components: {
755
919
  ...global.admin?.components,
756
920
  elements: {
@@ -3207,12 +3371,15 @@ __export(nextjs_exports, {
3207
3371
 
3208
3372
  // src/nextjs/client/payload.ts
3209
3373
  var import_payload = require("payload");
3210
- var payloadPromise = null;
3211
3374
  var WEBSITE_CONTENT_TAG = "website-content";
3212
3375
  function createPayloadClient(config) {
3376
+ let payloadPromise = null;
3213
3377
  return function getPayloadClient() {
3214
3378
  if (!payloadPromise) {
3215
- 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
+ });
3216
3383
  }
3217
3384
  return payloadPromise;
3218
3385
  };
@@ -3570,7 +3737,9 @@ var defaultBuilderBlockSettingsV2 = {
3570
3737
  },
3571
3738
  typography: {
3572
3739
  bodyAlign: "left",
3740
+ bodySizePt: null,
3573
3741
  headingAlign: "left",
3742
+ headingSizePt: null,
3574
3743
  letterSpacingPreset: "normal",
3575
3744
  lineHeightPreset: "normal",
3576
3745
  maxTextWidth: "auto"
@@ -3599,7 +3768,9 @@ var defaultBuilderItemSettingsV2 = {
3599
3768
  },
3600
3769
  typography: {
3601
3770
  bodyAlign: "left",
3771
+ bodySizePt: null,
3602
3772
  headingAlign: "left",
3773
+ headingSizePt: null,
3603
3774
  letterSpacingPreset: "normal",
3604
3775
  lineHeightPreset: "normal",
3605
3776
  maxTextWidth: "auto"
@@ -3629,6 +3800,7 @@ var defaultBuilderThemeTokens = {
3629
3800
 
3630
3801
  // src/studio-pages/builder/adapters/settingsV2.ts
3631
3802
  var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3803
+ var isTextAlign = (value) => value === "left" || value === "center" || value === "right" || value === "justify";
3632
3804
  var parsePercent = (value) => {
3633
3805
  if (typeof value === "number" && Number.isFinite(value)) {
3634
3806
  return Math.max(0, Math.min(100, value));
@@ -3653,6 +3825,18 @@ var parsePixel = (value) => {
3653
3825
  }
3654
3826
  return null;
3655
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
+ };
3656
3840
  var mergeSettings = (defaults, input) => {
3657
3841
  if (!isRecord3(input)) {
3658
3842
  return structuredClone(defaults);
@@ -3671,6 +3855,10 @@ var mergeSettings = (defaults, input) => {
3671
3855
  };
3672
3856
  var legacyBlockToV2Settings = (block) => {
3673
3857
  const current = structuredClone(defaultBuilderBlockSettingsV2);
3858
+ if (block.blockType === "hero" && block.variant === "centered") {
3859
+ current.typography.headingAlign = "center";
3860
+ current.typography.bodyAlign = "center";
3861
+ }
3674
3862
  current.layout.contentWidth = block.contentWidth === "narrow" || block.contentWidth === "content" || block.contentWidth === "wide" || block.contentWidth === "full" || block.contentWidth === "inherit" ? block.contentWidth : current.layout.contentWidth;
3675
3863
  current.layout.sectionPaddingX = block.sectionPaddingX === "none" || block.sectionPaddingX === "sm" || block.sectionPaddingX === "md" || block.sectionPaddingX === "lg" || block.sectionPaddingX === "inherit" ? block.sectionPaddingX : current.layout.sectionPaddingX;
3676
3864
  current.layout.sectionPaddingY = block.sectionPaddingY === "none" || block.sectionPaddingY === "sm" || block.sectionPaddingY === "lg" ? block.sectionPaddingY : current.layout.sectionPaddingY;
@@ -3707,15 +3895,28 @@ var legacyBlockToV2Settings = (block) => {
3707
3895
  current.media.positionX = parsePercent(block.imagePositionX ?? block.backgroundImagePositionX);
3708
3896
  current.media.positionY = parsePercent(block.imagePositionY ?? block.backgroundImagePositionY);
3709
3897
  current.media.height = parsePixel(block.imageHeight);
3710
- current.typography.headingAlign = block.textHeadingAlign === "left" || block.textHeadingAlign === "center" || block.textHeadingAlign === "right" || block.textHeadingAlign === "justify" ? block.textHeadingAlign : current.typography.headingAlign;
3711
- 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);
3712
3902
  current.typography.maxTextWidth = block.textMaxWidth === "auto" || block.textMaxWidth === "sm" || block.textMaxWidth === "md" || block.textMaxWidth === "lg" || block.textMaxWidth === "full" ? block.textMaxWidth : current.typography.maxTextWidth;
3713
3903
  current.typography.lineHeightPreset = block.textLineHeightPreset === "tight" || block.textLineHeightPreset === "normal" || block.textLineHeightPreset === "relaxed" ? block.textLineHeightPreset : current.typography.lineHeightPreset;
3714
3904
  current.typography.letterSpacingPreset = block.textLetterSpacingPreset === "tight" || block.textLetterSpacingPreset === "normal" || block.textLetterSpacingPreset === "relaxed" ? block.textLetterSpacingPreset : current.typography.letterSpacingPreset;
3715
3905
  current.advanced.editCopyInPanel = Boolean(block.editCopyInPanel ?? current.advanced.editCopyInPanel);
3716
3906
  current.advanced.customClassName = typeof block.customClassName === "string" ? block.customClassName : current.advanced.customClassName;
3717
3907
  current.advanced.hideOnMobile = Boolean(block.hideOnMobile ?? current.advanced.hideOnMobile);
3718
- 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;
3719
3920
  };
3720
3921
  var v2SettingsToLegacyBlock = (blockWithSettings) => {
3721
3922
  const settings = legacyBlockToV2Settings(blockWithSettings);
@@ -3741,6 +3942,8 @@ var v2SettingsToLegacyBlock = (blockWithSettings) => {
3741
3942
  next.contentGradientAngle = settings.appearance.contentGradientAngle;
3742
3943
  next.textHeadingAlign = settings.typography.headingAlign;
3743
3944
  next.textBodyAlign = settings.typography.bodyAlign;
3945
+ next.textHeadingSizePt = settings.typography.headingSizePt;
3946
+ next.textBodySizePt = settings.typography.bodySizePt;
3744
3947
  next.textMaxWidth = settings.typography.maxTextWidth;
3745
3948
  next.textLineHeightPreset = settings.typography.lineHeightPreset;
3746
3949
  next.textLetterSpacingPreset = settings.typography.letterSpacingPreset;
@@ -3873,7 +4076,7 @@ function normalizePath(segments) {
3873
4076
  const cleaned = segments.map((segment) => segment.trim()).filter(Boolean).join("/");
3874
4077
  return cleaned.length > 0 ? `/${cleaned}` : "/";
3875
4078
  }
3876
- async function queryPageByPath(payload, path2, draft) {
4079
+ async function queryPageByPath(payload, path2, draft, collectionSlug, homeSlug) {
3877
4080
  const pathWhere = {
3878
4081
  path: {
3879
4082
  equals: path2
@@ -3885,7 +4088,7 @@ async function queryPageByPath(payload, path2, draft) {
3885
4088
  }
3886
4089
  };
3887
4090
  const result = await payload.find({
3888
- collection: "pages",
4091
+ collection: collectionSlug,
3889
4092
  depth: 2,
3890
4093
  draft,
3891
4094
  limit: 1,
@@ -3898,11 +4101,11 @@ async function queryPageByPath(payload, path2, draft) {
3898
4101
  if (path2 === "/") {
3899
4102
  const homeWhere = {
3900
4103
  slug: {
3901
- equals: "home"
4104
+ equals: homeSlug
3902
4105
  }
3903
4106
  };
3904
4107
  const homeResult = await payload.find({
3905
- collection: "pages",
4108
+ collection: collectionSlug,
3906
4109
  depth: 2,
3907
4110
  draft,
3908
4111
  limit: 1,
@@ -3913,27 +4116,31 @@ async function queryPageByPath(payload, path2, draft) {
3913
4116
  }
3914
4117
  return null;
3915
4118
  }
3916
- 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";
3917
4124
  const getPublishedPageByPathCached = (0, import_cache.unstable_cache)(
3918
4125
  async (path2) => {
3919
4126
  const payload = await getPayloadClient();
3920
- return queryPageByPath(payload, path2, false);
4127
+ return queryPageByPath(payload, path2, false, pagesCollectionSlug, homeSlug);
3921
4128
  },
3922
- ["page-by-path", PAGE_QUERY_CACHE_VERSION],
4129
+ ["page-by-path", PAGE_QUERY_CACHE_VERSION, pagesCollectionSlug, homeSlug, contentTag],
3923
4130
  { tags: [contentTag] }
3924
4131
  );
3925
4132
  async function getPageBySegments(segments, draft = false) {
3926
4133
  const path2 = normalizePath(segments);
3927
4134
  const payload = await getPayloadClient();
3928
4135
  if (draft) {
3929
- return queryPageByPath(payload, path2, true);
4136
+ return queryPageByPath(payload, path2, true, pagesCollectionSlug, homeSlug);
3930
4137
  }
3931
4138
  return getPublishedPageByPathCached(path2);
3932
4139
  }
3933
4140
  async function listPublishedPagePaths() {
3934
4141
  const payload = await getPayloadClient();
3935
4142
  const pages = await payload.find({
3936
- collection: "pages",
4143
+ collection: pagesCollectionSlug,
3937
4144
  depth: 0,
3938
4145
  draft: false,
3939
4146
  limit: 1e3,
@@ -3962,108 +4169,46 @@ function createPageQueries(getPayloadClient, contentTag = "website-content") {
3962
4169
 
3963
4170
  // src/nextjs/queries/site.ts
3964
4171
  var import_cache2 = require("next/cache");
3965
- function createSiteQueries(getPayloadClient, contentTag = "website-content") {
3966
- const getSiteSettingsCached = (0, import_cache2.unstable_cache)(
3967
- async () => {
3968
- const payload = await getPayloadClient();
3969
- const settings = await payload.findGlobal({
3970
- slug: "site-settings",
3971
- depth: 1
3972
- });
3973
- return settings;
3974
- },
3975
- ["site-settings-global"],
3976
- { tags: [contentTag] }
3977
- );
3978
- const getHeaderCached = (0, import_cache2.unstable_cache)(
3979
- async () => {
3980
- const payload = await getPayloadClient();
3981
- const header = await payload.findGlobal({
3982
- slug: "header",
3983
- depth: 1
3984
- });
3985
- return header;
3986
- },
3987
- ["header-global"],
3988
- { tags: [contentTag] }
3989
- );
3990
- const getFooterCached = (0, import_cache2.unstable_cache)(
3991
- async () => {
3992
- const payload = await getPayloadClient();
3993
- const footer = await payload.findGlobal({
3994
- slug: "footer",
3995
- depth: 1
3996
- });
3997
- return footer;
3998
- },
3999
- ["footer-global"],
4000
- { tags: [contentTag] }
4001
- );
4002
- const getSocialMediaCached = (0, import_cache2.unstable_cache)(
4003
- async () => {
4004
- const payload = await getPayloadClient();
4005
- const socialMedia = await payload.findGlobal({
4006
- slug: "social-media",
4007
- depth: 1
4008
- });
4009
- return socialMedia;
4010
- },
4011
- ["social-media-global"],
4012
- { tags: [contentTag] }
4013
- );
4014
- async function getSiteSettings(draft = false) {
4015
- if (draft) {
4016
- const payload = await getPayloadClient();
4017
- const settings = await payload.findGlobal({
4018
- slug: "site-settings",
4019
- depth: 1,
4020
- draft: true
4021
- });
4022
- return settings;
4023
- }
4024
- return getSiteSettingsCached();
4025
- }
4026
- async function getHeader(draft = false) {
4027
- if (draft) {
4028
- const payload = await getPayloadClient();
4029
- const header = await payload.findGlobal({
4030
- slug: "header",
4031
- depth: 1,
4032
- draft: true
4033
- });
4034
- return header;
4035
- }
4036
- return getHeaderCached();
4037
- }
4038
- async function getFooter(draft = false) {
4039
- if (draft) {
4040
- const payload = await getPayloadClient();
4041
- const footer = await payload.findGlobal({
4042
- slug: "footer",
4043
- depth: 1,
4044
- draft: true
4045
- });
4046
- return footer;
4047
- }
4048
- return getFooterCached();
4049
- }
4050
- async function getSocialMedia(draft = false) {
4051
- if (draft) {
4052
- const payload = await getPayloadClient();
4053
- const socialMedia = await payload.findGlobal({
4054
- slug: "social-media",
4055
- depth: 1,
4056
- draft: true
4057
- });
4058
- return socialMedia;
4059
- }
4060
- 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
+ };
4061
4206
  }
4062
4207
  return {
4063
- getSiteSettings,
4064
- getHeader,
4065
- getFooter,
4066
- 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")
4067
4212
  };
4068
4213
  }
4069
4214
 
@@ -4119,9 +4264,13 @@ function resolveSocialMediaLinks(data) {
4119
4264
  // src/studio-pages/index.ts
4120
4265
  var studio_pages_exports = {};
4121
4266
  __export(studio_pages_exports, {
4267
+ DEFAULT_LEGACY_HERO_BACKGROUND_COLORS: () => DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
4122
4268
  createDefaultStudioDocument: () => createDefaultStudioDocument,
4269
+ createStudioPageFields: () => createStudioPageFields,
4123
4270
  createStudioPageService: () => createStudioPageService,
4271
+ createStudioPagesCollection: () => createStudioPagesCollection,
4124
4272
  defaultBuilderThemeTokens: () => defaultBuilderThemeTokens,
4273
+ defaultRelationshipHydrationMap: () => defaultRelationshipHydrationMap,
4125
4274
  getStudioDocumentFromPage: () => getStudioDocumentFromPage,
4126
4275
  layoutToStudioDocument: () => layoutToStudioDocument,
4127
4276
  pageInspectorPanels: () => pageInspectorPanels,
@@ -4164,8 +4313,52 @@ var alignOptions = [
4164
4313
  { label: "Right", value: "right" },
4165
4314
  { label: "Justify", value: "justify" }
4166
4315
  ];
4167
- 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
+ ];
4168
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
+ },
4169
4362
  {
4170
4363
  group: "typography",
4171
4364
  key: "settings.typography.headingAlign",
@@ -4382,6 +4575,13 @@ var inspectorDefinitionByBlockType = {
4382
4575
  { group: "basics", inlineEditable: true, key: "kicker", label: "Kicker", type: "text" },
4383
4576
  { group: "basics", inlineEditable: true, key: "headline", label: "Headline", type: "text" },
4384
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
+ },
4385
4585
  {
4386
4586
  group: "basics",
4387
4587
  key: "variant",
@@ -4508,6 +4708,13 @@ var inspectorDefinitionByBlockType = {
4508
4708
  };
4509
4709
 
4510
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
+ };
4511
4718
  var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
4512
4719
  var toRecordArray = (value) => Array.isArray(value) ? value.filter((item) => isRecord5(item)) : [];
4513
4720
  var getRelationID = (value) => {
@@ -4535,7 +4742,7 @@ var hydrateRelationship = (valueFromStudio, valueFromLayout) => {
4535
4742
  }
4536
4743
  return valueFromLayout;
4537
4744
  };
4538
- var hydrateDocumentWithLayoutRelations = (document, layout) => {
4745
+ var hydrateDocumentWithLayoutRelations = (document, layout, hydrationMap = defaultRelationshipHydrationMap) => {
4539
4746
  const nextNodes = document.nodes.map((node, index) => {
4540
4747
  const layoutBlock = layout[index];
4541
4748
  if (!isRecord5(layoutBlock)) {
@@ -4544,17 +4751,18 @@ var hydrateDocumentWithLayoutRelations = (document, layout) => {
4544
4751
  if (typeof layoutBlock.blockType !== "string" || layoutBlock.blockType !== node.type) {
4545
4752
  return node;
4546
4753
  }
4547
- const nextData = { ...node.data };
4548
- if (node.type === "hero") {
4549
- nextData.media = hydrateRelationship(nextData.media, layoutBlock.media);
4754
+ const rule = hydrationMap[node.type];
4755
+ if (!rule) {
4756
+ return node;
4550
4757
  }
4551
- if (node.type === "media") {
4552
- 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]);
4553
4761
  }
4554
- if (node.type === "featureGrid" || node.type === "logoWall" || node.type === "beforeAfter") {
4555
- const studioItems = Array.isArray(nextData.items) ? nextData.items : [];
4556
- const layoutItems = Array.isArray(layoutBlock.items) ? layoutBlock.items : [];
4557
- 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) => {
4558
4766
  if (!isRecord5(rawStudioItem)) {
4559
4767
  return rawStudioItem;
4560
4768
  }
@@ -4563,9 +4771,9 @@ var hydrateDocumentWithLayoutRelations = (document, layout) => {
4563
4771
  return rawStudioItem;
4564
4772
  }
4565
4773
  const nextItem = { ...rawStudioItem };
4566
- nextItem.media = hydrateRelationship(nextItem.media, layoutItem.media);
4567
- nextItem.beforeMedia = hydrateRelationship(nextItem.beforeMedia, layoutItem.beforeMedia);
4568
- nextItem.afterMedia = hydrateRelationship(nextItem.afterMedia, layoutItem.afterMedia);
4774
+ for (const itemField of itemFields) {
4775
+ nextItem[itemField] = hydrateRelationship(nextItem[itemField], layoutItem[itemField]);
4776
+ }
4569
4777
  return nextItem;
4570
4778
  });
4571
4779
  }
@@ -4584,7 +4792,12 @@ var normalizeDocument = (document) => ({
4584
4792
  schemaVersion: 1,
4585
4793
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4586
4794
  });
4587
- 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());
4588
4801
  const nodes = document.nodes.map((node) => {
4589
4802
  if (node.type !== "hero" || !isRecord5(node.data)) {
4590
4803
  return node;
@@ -4594,7 +4807,7 @@ var normalizeLegacyHeroDefaults = (document) => {
4594
4807
  const sectionBackgroundMode = typeof nextData.sectionBackgroundMode === "string" ? nextData.sectionBackgroundMode : "none";
4595
4808
  const hasBackgroundImageURL = typeof nextData.backgroundImageURL === "string" && nextData.backgroundImageURL.trim().length > 0;
4596
4809
  const mediaRelation = getRelationID(nextData.media);
4597
- if (heroBackgroundColor === "#124a37" && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
4810
+ if (normalizedLegacyColors.includes(heroBackgroundColor) && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
4598
4811
  nextData.backgroundColor = "";
4599
4812
  }
4600
4813
  return {
@@ -4613,13 +4826,13 @@ var assertCanPublish = (issues) => {
4613
4826
  throw new Error(`Cannot publish page: ${publishErrors[0].message}`);
4614
4827
  }
4615
4828
  };
4616
- var getStudioDocumentFromPage = (doc) => {
4829
+ var getStudioDocumentFromPage = (doc, relationshipHydration = defaultRelationshipHydrationMap) => {
4617
4830
  const title = typeof doc.title === "string" ? doc.title : void 0;
4618
4831
  const layout = asLayoutArray(doc.layout);
4619
4832
  try {
4620
4833
  const studioDocument = assertStudioDocumentV1(doc.studioDocument);
4621
4834
  if (layout.length > 0) {
4622
- return hydrateDocumentWithLayoutRelations(studioDocument, layout);
4835
+ return hydrateDocumentWithLayoutRelations(studioDocument, layout, relationshipHydration);
4623
4836
  }
4624
4837
  return studioDocument;
4625
4838
  } catch {
@@ -4631,8 +4844,10 @@ var getStudioDocumentFromPage = (doc) => {
4631
4844
  };
4632
4845
  var createStudioPageService = ({
4633
4846
  collectionSlug = "pages",
4847
+ legacyHeroBackgroundColors = DEFAULT_LEGACY_HERO_BACKGROUND_COLORS,
4634
4848
  modules,
4635
4849
  payload,
4850
+ relationshipHydration = defaultRelationshipHydrationMap,
4636
4851
  user
4637
4852
  }) => ({
4638
4853
  getPageForStudio: async (pageID) => {
@@ -4644,7 +4859,7 @@ var createStudioPageService = ({
4644
4859
  overrideAccess: false,
4645
4860
  user
4646
4861
  });
4647
- const studioDocument = getStudioDocumentFromPage(page);
4862
+ const studioDocument = getStudioDocumentFromPage(page, relationshipHydration);
4648
4863
  return {
4649
4864
  id: String(page.id),
4650
4865
  slug: typeof page.slug === "string" ? page.slug : "",
@@ -4654,7 +4869,10 @@ var createStudioPageService = ({
4654
4869
  },
4655
4870
  validateStudioDocument: (document) => validateStudioDocument(document, modules),
4656
4871
  saveDraft: async (pageID, document, _metadata) => {
4657
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
4872
+ const normalizedDocument = normalizeLegacyHeroDefaults(
4873
+ normalizeDocument(document),
4874
+ legacyHeroBackgroundColors
4875
+ );
4658
4876
  const compileResult = compileStudioDocument(normalizedDocument, modules);
4659
4877
  await payload.update({
4660
4878
  collection: collectionSlug,
@@ -4675,7 +4893,10 @@ var createStudioPageService = ({
4675
4893
  };
4676
4894
  },
4677
4895
  publish: async (pageID, document, _metadata) => {
4678
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
4896
+ const normalizedDocument = normalizeLegacyHeroDefaults(
4897
+ normalizeDocument(document),
4898
+ legacyHeroBackgroundColors
4899
+ );
4679
4900
  const compileResult = compileStudioDocument(normalizedDocument, modules);
4680
4901
  assertCanPublish(compileResult.issues);
4681
4902
  await payload.update({
@@ -4704,6 +4925,146 @@ var toEditorInitialDoc = (payloadPage) => ({
4704
4925
  title: payloadPage.title
4705
4926
  });
4706
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
+
4707
5068
  // src/studio-pages/migrations.ts
4708
5069
  var isRecord6 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
4709
5070
  var assertPageStudioDocumentV1 = (value) => {