@orion-studios/payload-studio 0.5.0-beta.98 → 0.6.0-beta.1

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 (63) hide show
  1. package/README.md +58 -68
  2. package/dist/admin/client.d.mts +5 -0
  3. package/dist/admin/client.d.ts +5 -0
  4. package/dist/admin/client.js +4491 -736
  5. package/dist/admin/client.mjs +3367 -752
  6. package/dist/admin/index.d.mts +2 -1
  7. package/dist/admin/index.d.ts +2 -1
  8. package/dist/admin/index.js +498 -53
  9. package/dist/admin/index.mjs +2 -1
  10. package/dist/admin-app/client.d.mts +1 -0
  11. package/dist/admin-app/client.d.ts +1 -0
  12. package/dist/admin-app/client.js +285 -109
  13. package/dist/admin-app/client.mjs +59 -871
  14. package/dist/admin-app/index.d.mts +2 -1
  15. package/dist/admin-app/index.d.ts +2 -1
  16. package/dist/admin-app/index.mjs +5 -3
  17. package/dist/admin-app/styles.css +1708 -56
  18. package/dist/admin.css +158 -35
  19. package/dist/blocks/index.js +415 -200
  20. package/dist/blocks/index.mjs +2 -2
  21. package/dist/{chunk-XK3K5GRP.mjs → chunk-JQAHXYAM.mjs} +271 -67
  22. package/dist/chunk-KPIX7OSV.mjs +1051 -0
  23. package/dist/chunk-OQSEJXC4.mjs +166 -0
  24. package/dist/{chunk-XHWQJUX5.mjs → chunk-OTHERBGX.mjs} +3 -3
  25. package/dist/chunk-PF3EBZXF.mjs +326 -0
  26. package/dist/chunk-Q2HGC67S.mjs +904 -0
  27. package/dist/{chunk-XVH5SCBD.mjs → chunk-RKTIFEUY.mjs} +4 -19
  28. package/dist/chunk-W2UOCJDX.mjs +32 -0
  29. package/dist/{chunk-C4J35SPJ.mjs → chunk-XKUTZ7IU.mjs} +257 -452
  30. package/dist/{index-ZbOx4OCF.d.ts → index-52HdVLQq.d.ts} +12 -22
  31. package/dist/index-BMitiKK8.d.ts +435 -0
  32. package/dist/index-Crx_MtPw.d.ts +223 -0
  33. package/dist/index-Cv-6qnrw.d.mts +223 -0
  34. package/dist/{index-ZbOx4OCF.d.mts → index-DEQC3Dwj.d.mts} +12 -22
  35. package/dist/{index-BIwu3qIH.d.mts → index-DWmudwDm.d.mts} +2 -1
  36. package/dist/{index-BIwu3qIH.d.ts → index-DWmudwDm.d.ts} +2 -1
  37. package/dist/index-D_b24Gef.d.mts +435 -0
  38. package/dist/index.d.mts +5 -4
  39. package/dist/index.d.ts +5 -4
  40. package/dist/index.js +1968 -1198
  41. package/dist/index.mjs +10 -8
  42. package/dist/nextjs/index.js +5 -684
  43. package/dist/nextjs/index.mjs +2 -3
  44. package/dist/sitePreviewTypes-BkHCWxNW.d.mts +58 -0
  45. package/dist/sitePreviewTypes-BkHCWxNW.d.ts +58 -0
  46. package/dist/studio/index.d.mts +1 -1
  47. package/dist/studio/index.d.ts +1 -1
  48. package/dist/studio-pages/builder.css +125 -83
  49. package/dist/studio-pages/client.d.mts +58 -1
  50. package/dist/studio-pages/client.d.ts +58 -1
  51. package/dist/studio-pages/client.js +450 -241
  52. package/dist/studio-pages/client.mjs +455 -247
  53. package/dist/studio-pages/index.d.mts +3 -2
  54. package/dist/studio-pages/index.d.ts +3 -2
  55. package/dist/studio-pages/index.js +418 -183
  56. package/dist/studio-pages/index.mjs +15 -6
  57. package/package.json +19 -5
  58. package/dist/chunk-2FO2ROW4.mjs +0 -468
  59. package/dist/chunk-SIL2J5MF.mjs +0 -155
  60. package/dist/index-BFXZue5i.d.ts +0 -178
  61. package/dist/index-CoYRBbf6.d.mts +0 -178
  62. package/dist/index-R7hA134j.d.mts +0 -140
  63. package/dist/index-vjrjy0P4.d.ts +0 -140
package/dist/index.js CHANGED
@@ -79,6 +79,143 @@ var createThemePreferenceField = (defaultTheme = "brand-light") => ({
79
79
  });
80
80
  var themePreferenceField = createThemePreferenceField("brand-light");
81
81
 
82
+ // src/admin-app/routeRegistry.ts
83
+ var adminNavIcons = [
84
+ "dashboard",
85
+ "pages",
86
+ "forms",
87
+ "globals",
88
+ "media",
89
+ "tools",
90
+ "account",
91
+ "analytics"
92
+ ];
93
+ var roleCanAccessNav = (role, item) => {
94
+ if (!item.roles || item.roles.length === 0) {
95
+ return true;
96
+ }
97
+ if (!role) {
98
+ return false;
99
+ }
100
+ return item.roles.includes(role);
101
+ };
102
+ var navItemIsActive = (pathname, item) => {
103
+ if (item.href === "/admin") {
104
+ return pathname === "/admin";
105
+ }
106
+ return item.matchPrefixes.some((prefix) => pathname.startsWith(prefix));
107
+ };
108
+
109
+ // src/shared/studioSections.ts
110
+ var studioRoles = /* @__PURE__ */ new Set(["admin", "editor", "client"]);
111
+ var studioIcons = new Set(adminNavIcons);
112
+ var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
113
+ var isAbsoluteExternalURL = (value) => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(value) || value.startsWith("//");
114
+ var normalizePathLikeValue = (value) => {
115
+ const trimmed = value.trim();
116
+ if (!trimmed) {
117
+ return "";
118
+ }
119
+ if (isAbsoluteExternalURL(trimmed)) {
120
+ return trimmed;
121
+ }
122
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
123
+ const normalized = withLeadingSlash.replace(/\/+$/, "");
124
+ return normalized || "/";
125
+ };
126
+ var normalizeStringArray = (value) => {
127
+ if (!Array.isArray(value)) {
128
+ return [];
129
+ }
130
+ return value.filter((entry) => typeof entry === "string").map((entry) => normalizePathLikeValue(entry)).filter((entry) => entry.length > 0);
131
+ };
132
+ var normalizeRoles = (value) => {
133
+ if (!Array.isArray(value)) {
134
+ return void 0;
135
+ }
136
+ const roles = value.filter((entry) => typeof entry === "string" && studioRoles.has(entry));
137
+ return roles.length > 0 ? roles : void 0;
138
+ };
139
+ var normalizeCard = (value) => {
140
+ if (!isRecord(value) || typeof value.title !== "string") {
141
+ return void 0;
142
+ }
143
+ const title = value.title.trim();
144
+ if (!title) {
145
+ return void 0;
146
+ }
147
+ return {
148
+ title,
149
+ ...typeof value.description === "string" && value.description.trim().length > 0 ? { description: value.description.trim() } : {}
150
+ };
151
+ };
152
+ var normalizeIcon = (value) => typeof value === "string" && studioIcons.has(value) ? value : void 0;
153
+ var resolveStudioSections = (value) => {
154
+ if (!Array.isArray(value)) {
155
+ return [];
156
+ }
157
+ const sections = [];
158
+ const seen = /* @__PURE__ */ new Set();
159
+ for (const entry of value) {
160
+ if (!isRecord(entry) || typeof entry.id !== "string" || typeof entry.label !== "string") {
161
+ continue;
162
+ }
163
+ const id = entry.id.trim();
164
+ const label = entry.label.trim();
165
+ if (!id || !label || seen.has(id)) {
166
+ continue;
167
+ }
168
+ const href = typeof entry.href === "string" && entry.href.trim().length > 0 ? normalizePathLikeValue(entry.href) : isRecord(entry.view) && typeof entry.view.path === "string" ? normalizePathLikeValue(entry.view.path) : "";
169
+ if (!href) {
170
+ continue;
171
+ }
172
+ const matchPrefixes = Array.from(/* @__PURE__ */ new Set([href, ...normalizeStringArray(entry.matchPrefixes)]));
173
+ sections.push({
174
+ id,
175
+ label,
176
+ href,
177
+ matchPrefixes,
178
+ ...normalizeIcon(entry.icon) ? { icon: normalizeIcon(entry.icon) } : {},
179
+ ...normalizeRoles(entry.roles) ? { roles: normalizeRoles(entry.roles) } : {},
180
+ ...normalizeCard(entry.card) ? { card: normalizeCard(entry.card) } : {}
181
+ });
182
+ seen.add(id);
183
+ }
184
+ return sections;
185
+ };
186
+ var resolveStudioSectionViews = (value) => {
187
+ if (!Array.isArray(value)) {
188
+ return {};
189
+ }
190
+ const views = {};
191
+ for (const entry of value) {
192
+ if (!isRecord(entry) || typeof entry.id !== "string" || !isRecord(entry.view)) {
193
+ continue;
194
+ }
195
+ const id = entry.id.trim();
196
+ const view = entry.view;
197
+ const component = isRecord(view.Component) ? view.Component : null;
198
+ if (!id || typeof view.path !== "string" || !component || typeof component.exportName !== "string" || typeof component.path !== "string") {
199
+ continue;
200
+ }
201
+ const path2 = normalizePathLikeValue(view.path);
202
+ const componentPath = component.path.trim();
203
+ const exportName = component.exportName.trim();
204
+ if (!path2 || !componentPath || !exportName) {
205
+ continue;
206
+ }
207
+ views[id] = {
208
+ path: path2,
209
+ Component: {
210
+ exportName,
211
+ path: componentPath,
212
+ ...isRecord(component.clientProps) ? { clientProps: component.clientProps } : {}
213
+ }
214
+ };
215
+ }
216
+ return views;
217
+ };
218
+
82
219
  // src/admin/helpers/configureAdmin.ts
83
220
  var import_meta = {};
84
221
  function getPkgDistDir() {
@@ -104,17 +241,28 @@ function configureAdmin(config) {
104
241
  brandPrimary = "#3b82f6",
105
242
  brandSecondary = "#8b5cf6",
106
243
  defaultTheme = "brand-light",
107
- logoUrl
244
+ logoUrl,
245
+ allowThemePreference = false
108
246
  } = config;
109
247
  const studioEnabled = config.studio?.enabled ?? true;
248
+ const formsEnabled = config.studio?.forms?.enabled ?? false;
249
+ const formsCollectionSlug = config.studio?.forms?.collectionSlug || "forms";
250
+ const formSubmissionsCollectionSlug = config.studio?.forms?.submissionsCollectionSlug || "form-submissions";
251
+ const formUploadsCollectionSlug = config.studio?.forms?.uploadsCollectionSlug || "form-uploads";
110
252
  const pagesCollectionSlug = config.studio?.pages?.collectionSlug || "pages";
253
+ const builderBasePath = config.studio?.pages?.builderBasePath || "/builder";
111
254
  const mediaCollectionSlug = config.studio?.media?.collectionSlug || "media";
112
- const contactFormStudioPath = "/studio-contact-form";
255
+ const globalsBasePath = "/site-globals";
256
+ const pagesBasePath = "/pages";
257
+ const formsBasePath = "/forms";
258
+ const mediaBasePath = "/media";
259
+ const toolsBasePath = "/tools";
260
+ const contactFormStudioPath = "/contact-form";
113
261
  const configuredGlobals = config.studio?.globals || [
114
262
  { slug: "site-settings", label: "Website Settings" },
115
263
  { slug: "header", label: "Header & Navigation" },
116
264
  { slug: "footer", label: "Footer" },
117
- { slug: "contact-form", label: "Contact Form" }
265
+ { slug: "social-media", label: "Social Media" }
118
266
  ];
119
267
  const globals = configuredGlobals.map((global) => {
120
268
  if (global.slug !== "contact-form" || global.href) {
@@ -125,24 +273,80 @@ function configureAdmin(config) {
125
273
  href: contactFormStudioPath
126
274
  };
127
275
  });
276
+ const studioSections = resolveStudioSections(config.studio?.sections || []);
277
+ const studioSectionViews = resolveStudioSectionViews(config.studio?.sections || []);
278
+ const sitePreview = config.studio?.sitePreview;
128
279
  let cssPath;
129
280
  const pkgDist = getPkgDistDir();
130
281
  const sourceCssPath = import_path.default.resolve(pkgDist, "admin.css");
131
- if (config.basePath && import_fs.default.existsSync(sourceCssPath)) {
132
- let css = import_fs.default.readFileSync(sourceCssPath, "utf-8");
133
- css = css.replace("--brand-primary: #3b82f6;", `--brand-primary: ${brandPrimary};`);
134
- css = css.replace("--brand-secondary: #8b5cf6;", `--brand-secondary: ${brandSecondary};`);
135
- const genDir = import_path.default.resolve(config.basePath, ".generated");
282
+ const adminAppCssPath = import_path.default.resolve(pkgDist, "admin-app", "styles.css");
283
+ const cssSources = [sourceCssPath, adminAppCssPath].filter((filePath) => import_fs.default.existsSync(filePath));
284
+ if (cssSources.length === 0) {
285
+ cssPath = sourceCssPath;
286
+ } 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");
136
298
  if (!import_fs.default.existsSync(genDir)) {
137
299
  import_fs.default.mkdirSync(genDir, { recursive: true });
138
300
  }
139
301
  const genPath = import_path.default.resolve(genDir, "admin.css");
140
302
  import_fs.default.writeFileSync(genPath, css);
141
303
  cssPath = genPath;
142
- } else {
143
- cssPath = sourceCssPath;
144
304
  }
145
305
  const clientPath = "@orion-studios/payload-studio/admin/client";
306
+ const studioNavClientProps = {
307
+ brandName,
308
+ formSubmissionsCollectionSlug,
309
+ formsCollectionSlug,
310
+ formsEnabled,
311
+ formUploadsCollectionSlug,
312
+ globalsBasePath,
313
+ globalsExtraMatchPrefixes: [contactFormStudioPath],
314
+ logoUrl,
315
+ mediaCollectionSlug,
316
+ pagesCollectionSlug,
317
+ sections: studioSections
318
+ };
319
+ const studioBackBreadcrumbComponent = {
320
+ exportName: "StudioBackBreadcrumb",
321
+ path: clientPath
322
+ };
323
+ const hasMatchingComponent = (items, exportName) => Array.isArray(items) && items.some(
324
+ (item) => item && typeof item === "object" && item.exportName === exportName && item.path === clientPath
325
+ );
326
+ const appendComponent = (items, component, exportName) => hasMatchingComponent(items, exportName) ? items || [] : [...items || [], component];
327
+ const attachStudioBackBreadcrumbToCollection = (collection) => {
328
+ if (!studioEnabled) {
329
+ return collection;
330
+ }
331
+ const existingBeforeDocumentControls = collection.admin?.components?.edit?.beforeDocumentControls;
332
+ return {
333
+ ...collection,
334
+ admin: {
335
+ ...collection.admin,
336
+ components: {
337
+ ...collection.admin?.components,
338
+ edit: {
339
+ ...collection.admin?.components?.edit,
340
+ beforeDocumentControls: appendComponent(
341
+ existingBeforeDocumentControls,
342
+ studioBackBreadcrumbComponent,
343
+ "StudioBackBreadcrumb"
344
+ )
345
+ }
346
+ }
347
+ }
348
+ };
349
+ };
146
350
  return {
147
351
  admin: {
148
352
  css: cssPath,
@@ -151,14 +355,7 @@ function configureAdmin(config) {
151
355
  Nav: {
152
356
  exportName: "AdminStudioNav",
153
357
  path: clientPath,
154
- clientProps: {
155
- brandName,
156
- logoUrl,
157
- globalsBasePath: "/studio-globals",
158
- globalsExtraMatchPrefixes: [contactFormStudioPath],
159
- mediaCollectionSlug,
160
- pagesCollectionSlug
161
- }
358
+ clientProps: studioNavClientProps
162
359
  }
163
360
  } : {},
164
361
  graphics: {
@@ -184,39 +381,131 @@ function configureAdmin(config) {
184
381
  Component: {
185
382
  exportName: studioEnabled ? "AdminStudioDashboard" : "Dashboard",
186
383
  path: clientPath,
187
- clientProps: {
188
- brandName,
189
- logoUrl,
190
- globalsBasePath: "/studio-globals",
191
- globalsExtraMatchPrefixes: [contactFormStudioPath],
192
- mediaCollectionSlug,
193
- pagesCollectionSlug
194
- }
384
+ clientProps: studioNavClientProps
195
385
  }
196
386
  },
197
387
  ...studioEnabled ? {
198
388
  studioGlobals: {
199
- path: "/studio-globals",
389
+ path: globalsBasePath,
200
390
  Component: {
201
391
  exportName: "AdminStudioGlobalsView",
202
392
  path: clientPath,
203
393
  clientProps: {
394
+ ...studioNavClientProps,
204
395
  globals,
205
- globalsBasePath: "/studio-globals"
396
+ globalsBasePath
397
+ }
398
+ }
399
+ },
400
+ studioPages: {
401
+ path: pagesBasePath,
402
+ Component: {
403
+ exportName: "AdminStudioPagesListView",
404
+ path: clientPath,
405
+ clientProps: {
406
+ ...studioNavClientProps,
407
+ pagesCollectionSlug
408
+ }
409
+ }
410
+ },
411
+ studioPageEditor: {
412
+ path: `${pagesBasePath}/:id`,
413
+ Component: {
414
+ exportName: "AdminStudioPageEditView",
415
+ path: clientPath,
416
+ clientProps: {
417
+ ...studioNavClientProps,
418
+ builderBasePath
419
+ }
420
+ }
421
+ },
422
+ studioPageNew: {
423
+ path: `${pagesBasePath}/new`,
424
+ Component: {
425
+ exportName: "AdminStudioNewPageView",
426
+ path: clientPath,
427
+ clientProps: {
428
+ ...studioNavClientProps,
429
+ pagesCollectionSlug
206
430
  }
207
431
  }
208
432
  },
209
433
  studioContactForm: {
210
- path: "/studio-contact-form",
434
+ path: contactFormStudioPath,
211
435
  Component: {
212
436
  exportName: "AdminStudioContactFormView",
213
437
  path: clientPath,
214
438
  clientProps: {
439
+ ...studioNavClientProps,
215
440
  globalSlug: "contact-form",
216
- globalsBasePath: "/studio-globals"
441
+ globalsBasePath
217
442
  }
218
443
  }
219
- }
444
+ },
445
+ ...formsEnabled ? {
446
+ studioForms: {
447
+ path: formsBasePath,
448
+ Component: {
449
+ exportName: "AdminStudioFormsView",
450
+ path: clientPath,
451
+ clientProps: {
452
+ ...studioNavClientProps,
453
+ formsCollectionSlug,
454
+ formSubmissionsCollectionSlug,
455
+ formUploadsCollectionSlug
456
+ }
457
+ }
458
+ }
459
+ } : {},
460
+ studioMedia: {
461
+ path: mediaBasePath,
462
+ Component: {
463
+ exportName: "AdminStudioMediaView",
464
+ path: clientPath,
465
+ clientProps: {
466
+ ...studioNavClientProps,
467
+ mediaCollectionSlug
468
+ }
469
+ }
470
+ },
471
+ studioMediaItem: {
472
+ path: `${mediaBasePath}/:id`,
473
+ Component: {
474
+ exportName: "AdminStudioMediaItemView",
475
+ path: clientPath,
476
+ clientProps: {
477
+ ...studioNavClientProps,
478
+ mediaCollectionSlug
479
+ }
480
+ }
481
+ },
482
+ studioTools: {
483
+ path: toolsBasePath,
484
+ Component: {
485
+ exportName: "AdminStudioToolsView",
486
+ path: clientPath,
487
+ clientProps: {
488
+ ...studioNavClientProps,
489
+ mediaCollectionSlug,
490
+ pagesCollectionSlug
491
+ }
492
+ }
493
+ },
494
+ ...Object.fromEntries(
495
+ Object.entries(studioSectionViews).map(([id, view]) => [
496
+ id,
497
+ {
498
+ path: view.path,
499
+ Component: {
500
+ ...view.Component,
501
+ clientProps: {
502
+ ...studioNavClientProps,
503
+ ...view.Component.clientProps || {}
504
+ }
505
+ }
506
+ }
507
+ ])
508
+ )
220
509
  } : {}
221
510
  },
222
511
  providers: [
@@ -224,19 +513,33 @@ function configureAdmin(config) {
224
513
  exportName: "ThemeProvider",
225
514
  path: clientPath,
226
515
  clientProps: {
516
+ allowThemePreference,
227
517
  defaultTheme
228
518
  }
229
519
  }
230
520
  ],
231
- afterNavLinks: [
521
+ beforeLogin: [
232
522
  {
233
- exportName: "ThemeSwitcher",
523
+ exportName: "AdminLoginIntro",
234
524
  path: clientPath,
235
525
  clientProps: {
236
- defaultTheme
526
+ brandName,
527
+ logoUrl
237
528
  }
238
529
  }
239
- ]
530
+ ],
531
+ ...allowThemePreference ? {
532
+ afterNavLinks: [
533
+ {
534
+ exportName: "ThemeSwitcher",
535
+ path: clientPath,
536
+ clientProps: {
537
+ allowThemePreference,
538
+ defaultTheme
539
+ }
540
+ }
541
+ ]
542
+ } : {}
240
543
  },
241
544
  meta: {
242
545
  titleSuffix: ` \u2014 ${brandName}`
@@ -251,11 +554,77 @@ function configureAdmin(config) {
251
554
  const hasThemePreference = existingFields.some(
252
555
  (field) => typeof field === "object" && field !== null && "name" in field && field.name === "themePreference"
253
556
  );
254
- return {
557
+ const nextCollection = {
255
558
  ...usersCollection,
256
- fields: hasThemePreference ? existingFields : [...existingFields, createThemePreferenceField(defaultTheme)]
559
+ fields: !allowThemePreference || hasThemePreference ? existingFields : [...existingFields, createThemePreferenceField(defaultTheme)]
560
+ };
561
+ return attachStudioBackBreadcrumbToCollection(nextCollection);
562
+ },
563
+ wrapPagesCollection(pagesCollection) {
564
+ if (!studioEnabled) {
565
+ return pagesCollection;
566
+ }
567
+ const collectionWithBreadcrumb = attachStudioBackBreadcrumbToCollection(pagesCollection);
568
+ const existingEditMenuItems = collectionWithBreadcrumb.admin?.components?.edit?.editMenuItems;
569
+ const existingViews = collectionWithBreadcrumb.admin?.components?.views;
570
+ const existingEditViews = existingViews?.edit;
571
+ const hasCustomEditView = Boolean(
572
+ existingEditViews?.root || existingEditViews?.default && typeof existingEditViews.default === "object" && existingEditViews.default.Component
573
+ );
574
+ return {
575
+ ...collectionWithBreadcrumb,
576
+ admin: {
577
+ ...collectionWithBreadcrumb.admin,
578
+ components: {
579
+ ...collectionWithBreadcrumb.admin?.components,
580
+ edit: {
581
+ ...collectionWithBreadcrumb.admin?.components?.edit,
582
+ editMenuItems: appendComponent(
583
+ existingEditMenuItems,
584
+ {
585
+ exportName: "OpenInStudioMenuItem",
586
+ path: clientPath,
587
+ clientProps: {
588
+ pagesPathBase: pagesBasePath
589
+ }
590
+ },
591
+ "OpenInStudioMenuItem"
592
+ )
593
+ },
594
+ views: {
595
+ ...existingViews,
596
+ ...hasCustomEditView ? {} : {
597
+ edit: {
598
+ ...existingEditViews,
599
+ default: {
600
+ ...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
601
+ Component: {
602
+ exportName: "PageEditRedirectToStudio",
603
+ path: clientPath,
604
+ clientProps: {
605
+ pagesPathBase: pagesBasePath
606
+ }
607
+ }
608
+ }
609
+ }
610
+ }
611
+ }
612
+ }
613
+ }
257
614
  };
258
615
  },
616
+ wrapMediaCollection(mediaCollection) {
617
+ return attachStudioBackBreadcrumbToCollection(mediaCollection);
618
+ },
619
+ wrapFormsCollection(formsCollection) {
620
+ return attachStudioBackBreadcrumbToCollection(formsCollection);
621
+ },
622
+ wrapFormSubmissionsCollection(formSubmissionsCollection) {
623
+ return attachStudioBackBreadcrumbToCollection(formSubmissionsCollection);
624
+ },
625
+ wrapFormUploadsCollection(formUploadsCollection) {
626
+ return attachStudioBackBreadcrumbToCollection(formUploadsCollection);
627
+ },
259
628
  wrapGlobals(globals2) {
260
629
  const labelMap = {
261
630
  header: { group: "Site Design", label: "Header & Navigation" },
@@ -267,25 +636,110 @@ function configureAdmin(config) {
267
636
  return globals2.map((global) => {
268
637
  const mapping = labelMap[global.slug];
269
638
  if (!mapping) return global;
639
+ const shouldAttachSiteSettingsEditView = studioEnabled && global.slug === "site-settings";
640
+ const shouldAttachSocialMediaEditView = studioEnabled && global.slug === "social-media";
270
641
  const shouldAttachContactFormRedirect = studioEnabled && global.slug === "contact-form";
642
+ const shouldAttachHeaderEditView = studioEnabled && global.slug === "header";
643
+ const shouldAttachFooterEditView = studioEnabled && global.slug === "footer";
271
644
  const existingViews = global.admin?.components?.views;
272
645
  const existingEditViews = existingViews?.edit;
273
- const hasCustomContactFormEditView = Boolean(
646
+ const hasCustomEditView = Boolean(
274
647
  existingEditViews?.root || existingEditViews?.default && typeof existingEditViews.default === "object" && existingEditViews.default.Component
275
648
  );
276
- const contactFormEditViews = shouldAttachContactFormRedirect && !hasCustomContactFormEditView ? {
277
- ...existingEditViews,
278
- default: {
279
- ...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
280
- Component: {
281
- exportName: "StudioContactFormRedirect",
282
- path: clientPath,
283
- clientProps: {
284
- studioContactFormPath: contactFormStudioPath
649
+ const nextEditViews = (() => {
650
+ if (shouldAttachSiteSettingsEditView && !hasCustomEditView) {
651
+ return {
652
+ ...existingEditViews,
653
+ default: {
654
+ ...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
655
+ Component: {
656
+ exportName: "AdminStudioSiteSettingsGlobalView",
657
+ path: clientPath,
658
+ clientProps: {
659
+ ...studioNavClientProps,
660
+ globalSlug: global.slug,
661
+ mediaCollectionSlug
662
+ }
663
+ }
285
664
  }
286
- }
665
+ };
666
+ }
667
+ if (shouldAttachSocialMediaEditView && !hasCustomEditView) {
668
+ return {
669
+ ...existingEditViews,
670
+ default: {
671
+ ...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
672
+ Component: {
673
+ exportName: "AdminStudioSocialMediaGlobalView",
674
+ path: clientPath,
675
+ clientProps: {
676
+ ...studioNavClientProps,
677
+ globalSlug: global.slug
678
+ }
679
+ }
680
+ }
681
+ };
682
+ }
683
+ if (shouldAttachHeaderEditView && !hasCustomEditView) {
684
+ return {
685
+ ...existingEditViews,
686
+ default: {
687
+ ...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
688
+ Component: {
689
+ exportName: "AdminStudioHeaderGlobalView",
690
+ path: clientPath,
691
+ clientProps: {
692
+ ...studioNavClientProps,
693
+ actionHref: sitePreview?.header?.actionHref,
694
+ actionLabel: sitePreview?.header?.actionLabel,
695
+ globalSlug: global.slug,
696
+ locationSummary: sitePreview?.locationSummary,
697
+ pagesCollectionSlug
698
+ }
699
+ }
700
+ }
701
+ };
702
+ }
703
+ if (shouldAttachFooterEditView && !hasCustomEditView) {
704
+ return {
705
+ ...existingEditViews,
706
+ default: {
707
+ ...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
708
+ Component: {
709
+ exportName: "AdminStudioFooterGlobalView",
710
+ path: clientPath,
711
+ clientProps: {
712
+ ...studioNavClientProps,
713
+ builtByHref: sitePreview?.footer?.builtByHref,
714
+ builtByLabel: sitePreview?.footer?.builtByLabel,
715
+ description: sitePreview?.footer?.description,
716
+ footerCategories: sitePreview?.footer?.footerCategories,
717
+ footerLinks: sitePreview?.footer?.footerLinks,
718
+ globalSlug: global.slug,
719
+ locationSummary: sitePreview?.locationSummary
720
+ }
721
+ }
722
+ }
723
+ };
287
724
  }
288
- } : existingEditViews;
725
+ if (shouldAttachContactFormRedirect && !hasCustomEditView) {
726
+ return {
727
+ ...existingEditViews,
728
+ default: {
729
+ ...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
730
+ Component: {
731
+ exportName: "StudioContactFormRedirect",
732
+ path: clientPath,
733
+ clientProps: {
734
+ studioContactFormPath: contactFormStudioPath
735
+ }
736
+ }
737
+ }
738
+ };
739
+ }
740
+ return existingEditViews;
741
+ })();
742
+ const existingBeforeDocumentControls = global.admin?.components?.elements?.beforeDocumentControls;
289
743
  return {
290
744
  ...global,
291
745
  admin: {
@@ -293,12 +747,18 @@ function configureAdmin(config) {
293
747
  group: mapping.group,
294
748
  components: {
295
749
  ...global.admin?.components,
296
- ...shouldAttachContactFormRedirect ? {
750
+ elements: {
751
+ ...global.admin?.components?.elements,
752
+ beforeDocumentControls: studioEnabled ? appendComponent(
753
+ existingBeforeDocumentControls,
754
+ studioBackBreadcrumbComponent,
755
+ "StudioBackBreadcrumb"
756
+ ) : existingBeforeDocumentControls
757
+ },
758
+ ...nextEditViews ? {
297
759
  views: {
298
760
  ...existingViews,
299
- ...contactFormEditViews ? {
300
- edit: contactFormEditViews
301
- } : {}
761
+ edit: nextEditViews
302
762
  }
303
763
  } : {}
304
764
  }
@@ -765,23 +1225,6 @@ var parseAdminHeaderNavFromForm = (formData, pageOptions, maxRows = 24) => {
765
1225
  return [];
766
1226
  };
767
1227
 
768
- // src/admin-app/routeRegistry.ts
769
- var roleCanAccessNav = (role, item) => {
770
- if (!item.roles || item.roles.length === 0) {
771
- return true;
772
- }
773
- if (!role) {
774
- return false;
775
- }
776
- return item.roles.includes(role);
777
- };
778
- var navItemIsActive = (pathname, item) => {
779
- if (item.href === "/admin") {
780
- return pathname === "/admin";
781
- }
782
- return item.matchPrefixes.some((prefix) => pathname.startsWith(prefix));
783
- };
784
-
785
1228
  // src/blocks/index.ts
786
1229
  var blocks_exports = {};
787
1230
  __export(blocks_exports, {
@@ -818,165 +1261,176 @@ var sectionStyleDefaults = {
818
1261
  contentGradientAngle: "135",
819
1262
  contentGradientFrom: "#ffffff",
820
1263
  contentGradientPreset: "none",
821
- contentGradientTo: "#f4f6f2",
1264
+ contentGradientTo: "#f4f6f8",
822
1265
  contentWidth: "inherit",
823
1266
  sectionPaddingX: "inherit",
824
1267
  sectionBackgroundColor: "#ffffff",
825
1268
  sectionBackgroundMode: "none",
826
1269
  sectionGradientAngle: "135",
827
- sectionGradientFrom: "#124a37",
828
- sectionGradientPreset: "forest",
829
- sectionGradientTo: "#1f684f",
1270
+ sectionGradientFrom: "#334b63",
1271
+ sectionGradientPreset: "slate",
1272
+ sectionGradientTo: "#496582",
830
1273
  sectionPaddingY: "md",
831
1274
  sectionWidth: "content"
832
1275
  };
833
- var sectionStyleFields = () => [
834
- {
835
- name: "sectionWidth",
836
- type: "select",
837
- defaultValue: sectionStyleDefaults.sectionWidth,
838
- options: [
839
- { label: "Content", value: "content" },
840
- { label: "Wide", value: "wide" },
841
- { label: "Full", value: "full" }
842
- ]
843
- },
844
- {
845
- name: "contentWidth",
846
- type: "select",
847
- defaultValue: sectionStyleDefaults.contentWidth,
848
- options: [
849
- { label: "Inherit", value: "inherit" },
850
- { label: "Narrow", value: "narrow" },
851
- { label: "Content", value: "content" },
852
- { label: "Wide", value: "wide" },
853
- { label: "Full", value: "full" }
854
- ]
855
- },
856
- {
857
- name: "sectionPaddingY",
858
- type: "select",
859
- defaultValue: sectionStyleDefaults.sectionPaddingY,
860
- options: [
861
- { label: "None", value: "none" },
862
- { label: "Small", value: "sm" },
863
- { label: "Medium", value: "md" },
864
- { label: "Large", value: "lg" }
865
- ]
866
- },
867
- {
868
- name: "sectionPaddingX",
869
- type: "select",
870
- defaultValue: sectionStyleDefaults.sectionPaddingX,
871
- options: [
872
- { label: "Inherit", value: "inherit" },
873
- { label: "None", value: "none" },
874
- { label: "Small", value: "sm" },
875
- { label: "Medium", value: "md" },
876
- { label: "Large", value: "lg" }
877
- ]
878
- },
879
- {
880
- name: "sectionBackgroundMode",
881
- type: "select",
882
- defaultValue: sectionStyleDefaults.sectionBackgroundMode,
883
- options: [
884
- { label: "None", value: "none" },
885
- { label: "Color", value: "color" },
886
- { label: "Gradient", value: "gradient" }
887
- ]
888
- },
889
- {
890
- name: "sectionBackgroundColor",
891
- type: "text",
892
- defaultValue: sectionStyleDefaults.sectionBackgroundColor
893
- },
894
- {
895
- name: "sectionGradientPreset",
896
- type: "select",
897
- defaultValue: sectionStyleDefaults.sectionGradientPreset,
898
- options: [
899
- { label: "None", value: "none" },
900
- { label: "Forest", value: "forest" },
901
- { label: "Moss", value: "moss" },
902
- { label: "Cream", value: "cream" },
903
- { label: "Slate", value: "slate" }
904
- ]
905
- },
906
- {
907
- name: "sectionGradientFrom",
908
- type: "text",
909
- defaultValue: sectionStyleDefaults.sectionGradientFrom
910
- },
911
- {
912
- name: "sectionGradientTo",
913
- type: "text",
914
- defaultValue: sectionStyleDefaults.sectionGradientTo
915
- },
916
- {
917
- name: "sectionGradientAngle",
918
- type: "text",
919
- defaultValue: sectionStyleDefaults.sectionGradientAngle
920
- },
921
- {
922
- name: "contentBackgroundMode",
923
- type: "select",
924
- defaultValue: sectionStyleDefaults.contentBackgroundMode,
925
- options: [
926
- { label: "None", value: "none" },
927
- { label: "Color", value: "color" },
928
- { label: "Gradient", value: "gradient" }
929
- ]
930
- },
931
- {
932
- name: "contentBackgroundColor",
933
- type: "text",
934
- defaultValue: sectionStyleDefaults.contentBackgroundColor
935
- },
936
- {
937
- name: "contentGradientPreset",
938
- type: "select",
939
- defaultValue: sectionStyleDefaults.contentGradientPreset,
940
- options: [
941
- { label: "None", value: "none" },
942
- { label: "Cloud", value: "cloud" },
943
- { label: "Sand", value: "sand" },
944
- { label: "Mint", value: "mint" },
945
- { label: "Night", value: "night" }
946
- ]
947
- },
948
- {
949
- name: "contentGradientFrom",
950
- type: "text",
951
- defaultValue: sectionStyleDefaults.contentGradientFrom
952
- },
953
- {
954
- name: "contentGradientTo",
955
- type: "text",
956
- defaultValue: sectionStyleDefaults.contentGradientTo
957
- },
958
- {
959
- name: "contentGradientAngle",
960
- type: "text",
961
- defaultValue: sectionStyleDefaults.contentGradientAngle
962
- }
963
- ];
964
-
965
- // src/blocks/blocks/BeforeAfter.ts
966
- var BeforeAfterBlock = {
967
- slug: "beforeAfter",
968
- imageURL: "/images/project-before-2.svg",
969
- imageAltText: "Before and after section preview",
1276
+ var hideFromCMS = (field) => ({
1277
+ ...field,
970
1278
  admin: {
971
- components: {
972
- Label: builderBlockLabelComponent
973
- }
974
- },
975
- labels: {
976
- singular: "Before / After",
977
- plural: "Before / After"
978
- },
979
- fields: [
1279
+ ...field.admin || {},
1280
+ hidden: true
1281
+ }
1282
+ });
1283
+ var sectionStyleFields = () => {
1284
+ const fields = [
1285
+ {
1286
+ name: "sectionWidth",
1287
+ type: "select",
1288
+ defaultValue: sectionStyleDefaults.sectionWidth,
1289
+ options: [
1290
+ { label: "Content", value: "content" },
1291
+ { label: "Wide", value: "wide" },
1292
+ { label: "Full", value: "full" }
1293
+ ]
1294
+ },
1295
+ {
1296
+ name: "contentWidth",
1297
+ type: "select",
1298
+ defaultValue: sectionStyleDefaults.contentWidth,
1299
+ options: [
1300
+ { label: "Inherit", value: "inherit" },
1301
+ { label: "Narrow", value: "narrow" },
1302
+ { label: "Content", value: "content" },
1303
+ { label: "Wide", value: "wide" },
1304
+ { label: "Full", value: "full" }
1305
+ ]
1306
+ },
1307
+ {
1308
+ name: "sectionPaddingY",
1309
+ type: "select",
1310
+ defaultValue: sectionStyleDefaults.sectionPaddingY,
1311
+ options: [
1312
+ { label: "None", value: "none" },
1313
+ { label: "Small", value: "sm" },
1314
+ { label: "Medium", value: "md" },
1315
+ { label: "Large", value: "lg" }
1316
+ ]
1317
+ },
1318
+ {
1319
+ name: "sectionPaddingX",
1320
+ type: "select",
1321
+ defaultValue: sectionStyleDefaults.sectionPaddingX,
1322
+ options: [
1323
+ { label: "Inherit", value: "inherit" },
1324
+ { label: "None", value: "none" },
1325
+ { label: "Small", value: "sm" },
1326
+ { label: "Medium", value: "md" },
1327
+ { label: "Large", value: "lg" }
1328
+ ]
1329
+ },
1330
+ {
1331
+ name: "sectionBackgroundMode",
1332
+ type: "select",
1333
+ defaultValue: sectionStyleDefaults.sectionBackgroundMode,
1334
+ options: [
1335
+ { label: "None", value: "none" },
1336
+ { label: "Color", value: "color" },
1337
+ { label: "Gradient", value: "gradient" }
1338
+ ]
1339
+ },
1340
+ {
1341
+ name: "sectionBackgroundColor",
1342
+ type: "text",
1343
+ defaultValue: sectionStyleDefaults.sectionBackgroundColor
1344
+ },
1345
+ {
1346
+ name: "sectionGradientPreset",
1347
+ type: "select",
1348
+ defaultValue: sectionStyleDefaults.sectionGradientPreset,
1349
+ options: [
1350
+ { label: "None", value: "none" },
1351
+ { label: "Brand", value: "brand" },
1352
+ { label: "Forest", value: "forest" },
1353
+ { label: "Moss", value: "moss" },
1354
+ { label: "Cream", value: "cream" },
1355
+ { label: "Slate", value: "slate" }
1356
+ ]
1357
+ },
1358
+ {
1359
+ name: "sectionGradientFrom",
1360
+ type: "text",
1361
+ defaultValue: sectionStyleDefaults.sectionGradientFrom
1362
+ },
1363
+ {
1364
+ name: "sectionGradientTo",
1365
+ type: "text",
1366
+ defaultValue: sectionStyleDefaults.sectionGradientTo
1367
+ },
1368
+ {
1369
+ name: "sectionGradientAngle",
1370
+ type: "text",
1371
+ defaultValue: sectionStyleDefaults.sectionGradientAngle
1372
+ },
1373
+ {
1374
+ name: "contentBackgroundMode",
1375
+ type: "select",
1376
+ defaultValue: sectionStyleDefaults.contentBackgroundMode,
1377
+ options: [
1378
+ { label: "None", value: "none" },
1379
+ { label: "Color", value: "color" },
1380
+ { label: "Gradient", value: "gradient" }
1381
+ ]
1382
+ },
1383
+ {
1384
+ name: "contentBackgroundColor",
1385
+ type: "text",
1386
+ defaultValue: sectionStyleDefaults.contentBackgroundColor
1387
+ },
1388
+ {
1389
+ name: "contentGradientPreset",
1390
+ type: "select",
1391
+ defaultValue: sectionStyleDefaults.contentGradientPreset,
1392
+ options: [
1393
+ { label: "None", value: "none" },
1394
+ { label: "Cloud", value: "cloud" },
1395
+ { label: "Sand", value: "sand" },
1396
+ { label: "Mint", value: "mint" },
1397
+ { label: "Night", value: "night" }
1398
+ ]
1399
+ },
1400
+ {
1401
+ name: "contentGradientFrom",
1402
+ type: "text",
1403
+ defaultValue: sectionStyleDefaults.contentGradientFrom
1404
+ },
1405
+ {
1406
+ name: "contentGradientTo",
1407
+ type: "text",
1408
+ defaultValue: sectionStyleDefaults.contentGradientTo
1409
+ },
1410
+ {
1411
+ name: "contentGradientAngle",
1412
+ type: "text",
1413
+ defaultValue: sectionStyleDefaults.contentGradientAngle
1414
+ }
1415
+ ];
1416
+ return fields.map(hideFromCMS);
1417
+ };
1418
+
1419
+ // src/blocks/blocks/BeforeAfter.ts
1420
+ var BeforeAfterBlock = {
1421
+ slug: "beforeAfter",
1422
+ imageURL: "/images/project-before-2.svg",
1423
+ imageAltText: "Before and after section preview",
1424
+ admin: {
1425
+ components: {
1426
+ Label: builderBlockLabelComponent
1427
+ }
1428
+ },
1429
+ labels: {
1430
+ singular: "Before / After",
1431
+ plural: "Before / After"
1432
+ },
1433
+ fields: [
980
1434
  {
981
1435
  name: "title",
982
1436
  type: "text",
@@ -1014,12 +1468,26 @@ var BeforeAfterBlock = {
1014
1468
  relationTo: "media",
1015
1469
  required: false
1016
1470
  },
1471
+ {
1472
+ name: "beforeImageURL",
1473
+ type: "text",
1474
+ admin: {
1475
+ description: "Optional direct URL for the before image when using an external asset."
1476
+ }
1477
+ },
1017
1478
  {
1018
1479
  name: "afterMedia",
1019
1480
  type: "upload",
1020
1481
  relationTo: "media",
1021
1482
  required: false
1022
1483
  },
1484
+ {
1485
+ name: "afterImageURL",
1486
+ type: "text",
1487
+ admin: {
1488
+ description: "Optional direct URL for the after image when using an external asset."
1489
+ }
1490
+ },
1023
1491
  {
1024
1492
  name: "imageHeight",
1025
1493
  type: "number",
@@ -1175,6 +1643,10 @@ var CtaBlock = {
1175
1643
  name: "description",
1176
1644
  type: "textarea"
1177
1645
  },
1646
+ {
1647
+ name: "eyebrow",
1648
+ type: "text"
1649
+ },
1178
1650
  {
1179
1651
  name: "buttonLabel",
1180
1652
  type: "text"
@@ -1205,9 +1677,33 @@ var CtaBlock = {
1205
1677
  name: "backgroundColor",
1206
1678
  type: "text",
1207
1679
  admin: {
1208
- description: "Optional background color override for the CTA strip (example: #124a37)."
1680
+ description: "Optional background color override for the CTA strip (example: #334b63)."
1681
+ }
1682
+ },
1683
+ {
1684
+ name: "media",
1685
+ type: "upload",
1686
+ relationTo: "media",
1687
+ required: false
1688
+ },
1689
+ {
1690
+ name: "imageURL",
1691
+ type: "text",
1692
+ admin: {
1693
+ description: "Optional direct image URL when this CTA should use an external image."
1209
1694
  }
1210
1695
  },
1696
+ {
1697
+ name: "bullets",
1698
+ type: "array",
1699
+ fields: [
1700
+ {
1701
+ name: "label",
1702
+ type: "text",
1703
+ required: true
1704
+ }
1705
+ ]
1706
+ },
1211
1707
  {
1212
1708
  name: "settings",
1213
1709
  type: "json",
@@ -1235,6 +1731,10 @@ var FaqBlock = {
1235
1731
  plural: "FAQs"
1236
1732
  },
1237
1733
  fields: [
1734
+ {
1735
+ name: "eyebrow",
1736
+ type: "text"
1737
+ },
1238
1738
  {
1239
1739
  name: "title",
1240
1740
  type: "text",
@@ -1292,10 +1792,17 @@ var FeatureGridBlock = {
1292
1792
  plural: "Feature Grids"
1293
1793
  },
1294
1794
  fields: [
1795
+ {
1796
+ name: "eyebrow",
1797
+ type: "text"
1798
+ },
1295
1799
  {
1296
1800
  name: "title",
1297
- type: "text",
1298
- required: true
1801
+ type: "text"
1802
+ },
1803
+ {
1804
+ name: "subtitle",
1805
+ type: "textarea"
1299
1806
  },
1300
1807
  {
1301
1808
  name: "itemsPerRow",
@@ -1323,6 +1830,16 @@ var FeatureGridBlock = {
1323
1830
  name: "description",
1324
1831
  type: "textarea"
1325
1832
  },
1833
+ {
1834
+ name: "tone",
1835
+ type: "select",
1836
+ defaultValue: "warm",
1837
+ options: [
1838
+ { label: "Warm", value: "warm" },
1839
+ { label: "Cool", value: "cool" },
1840
+ { label: "Neutral", value: "neutral" }
1841
+ ]
1842
+ },
1326
1843
  {
1327
1844
  name: "iconType",
1328
1845
  type: "select",
@@ -1368,6 +1885,39 @@ var FeatureGridBlock = {
1368
1885
  relationTo: "media",
1369
1886
  required: false
1370
1887
  },
1888
+ {
1889
+ name: "imageURL",
1890
+ type: "text",
1891
+ admin: {
1892
+ description: "Optional direct image URL when this item should use an external image."
1893
+ }
1894
+ },
1895
+ {
1896
+ name: "embedURL",
1897
+ type: "text",
1898
+ admin: {
1899
+ description: "Optional iframe/embed URL for items like maps or other embedded content."
1900
+ }
1901
+ },
1902
+ {
1903
+ name: "buttonLabel",
1904
+ type: "text"
1905
+ },
1906
+ {
1907
+ name: "buttonHref",
1908
+ type: "text"
1909
+ },
1910
+ {
1911
+ name: "bullets",
1912
+ type: "array",
1913
+ fields: [
1914
+ {
1915
+ name: "label",
1916
+ type: "text",
1917
+ required: true
1918
+ }
1919
+ ]
1920
+ },
1371
1921
  {
1372
1922
  name: "imageHeight",
1373
1923
  type: "number",
@@ -1450,6 +2000,22 @@ var FeatureGridBlock = {
1450
2000
  {
1451
2001
  label: "Highlight",
1452
2002
  value: "highlight"
2003
+ },
2004
+ {
2005
+ label: "Split List",
2006
+ value: "splitList"
2007
+ },
2008
+ {
2009
+ label: "Panels",
2010
+ value: "panels"
2011
+ },
2012
+ {
2013
+ label: "Catalog",
2014
+ value: "catalog"
2015
+ },
2016
+ {
2017
+ label: "Contact Split",
2018
+ value: "contact"
1453
2019
  }
1454
2020
  ]
1455
2021
  },
@@ -1457,7 +2023,7 @@ var FeatureGridBlock = {
1457
2023
  name: "backgroundColor",
1458
2024
  type: "text",
1459
2025
  admin: {
1460
- description: "Optional background color override when using the Highlight variant (example: #1f684f)."
2026
+ description: "Optional background color override when using the Highlight variant (example: #334b63)."
1461
2027
  }
1462
2028
  },
1463
2029
  {
@@ -1487,6 +2053,10 @@ var FormEmbedBlock = {
1487
2053
  plural: "Form Embeds"
1488
2054
  },
1489
2055
  fields: [
2056
+ {
2057
+ name: "eyebrow",
2058
+ type: "text"
2059
+ },
1490
2060
  {
1491
2061
  name: "title",
1492
2062
  type: "text"
@@ -1495,6 +2065,10 @@ var FormEmbedBlock = {
1495
2065
  name: "description",
1496
2066
  type: "textarea"
1497
2067
  },
2068
+ {
2069
+ name: "submitLabel",
2070
+ type: "text"
2071
+ },
1498
2072
  {
1499
2073
  name: "formType",
1500
2074
  type: "select",
@@ -1576,6 +2150,13 @@ var HeroBlock = {
1576
2150
  type: "upload",
1577
2151
  relationTo: "media"
1578
2152
  },
2153
+ {
2154
+ name: "backgroundImageURL",
2155
+ type: "text",
2156
+ admin: {
2157
+ description: "Optional direct image URL when the hero should use an external image source."
2158
+ }
2159
+ },
1579
2160
  {
1580
2161
  name: "backgroundImageFit",
1581
2162
  type: "select",
@@ -1621,7 +2202,7 @@ var HeroBlock = {
1621
2202
  name: "backgroundColor",
1622
2203
  type: "text",
1623
2204
  admin: {
1624
- description: "Optional background color override (example: #124a37)."
2205
+ description: "Optional background color override (example: #334b63)."
1625
2206
  }
1626
2207
  },
1627
2208
  {
@@ -1659,14 +2240,14 @@ var HeroBlock = {
1659
2240
  name: "backgroundOverlayGradientFrom",
1660
2241
  type: "text",
1661
2242
  admin: {
1662
- description: "Gradient overlay start color (example: #0d4a37). Used when Overlay Mode is Gradient."
2243
+ description: "Gradient overlay start color (example: #334b63). Used when Overlay Mode is Gradient."
1663
2244
  }
1664
2245
  },
1665
2246
  {
1666
2247
  name: "backgroundOverlayGradientTo",
1667
2248
  type: "text",
1668
2249
  admin: {
1669
- description: "Gradient overlay end color (example: #1f684f). Used when Overlay Mode is Gradient."
2250
+ description: "Gradient overlay end color (example: #496582). Used when Overlay Mode is Gradient."
1670
2251
  }
1671
2252
  },
1672
2253
  {
@@ -1821,6 +2402,13 @@ var LogoWallBlock = {
1821
2402
  relationTo: "media",
1822
2403
  required: false
1823
2404
  },
2405
+ {
2406
+ name: "imageURL",
2407
+ type: "text",
2408
+ admin: {
2409
+ description: "Optional direct image URL for this logo when using an external asset."
2410
+ }
2411
+ },
1824
2412
  {
1825
2413
  name: "imageHeight",
1826
2414
  type: "number",
@@ -1926,7 +2514,14 @@ var MediaBlock = {
1926
2514
  name: "image",
1927
2515
  type: "upload",
1928
2516
  relationTo: "media",
1929
- required: true
2517
+ required: false
2518
+ },
2519
+ {
2520
+ name: "imageURL",
2521
+ type: "text",
2522
+ admin: {
2523
+ description: "Optional direct image URL when this section should use an external image."
2524
+ }
1930
2525
  },
1931
2526
  {
1932
2527
  name: "caption",
@@ -2004,6 +2599,21 @@ var RichTextBlock = {
2004
2599
  plural: "Rich Text Sections"
2005
2600
  },
2006
2601
  fields: [
2602
+ {
2603
+ name: "variant",
2604
+ type: "select",
2605
+ defaultValue: "default",
2606
+ options: [
2607
+ {
2608
+ label: "Default",
2609
+ value: "default"
2610
+ },
2611
+ {
2612
+ label: "Quote Banner",
2613
+ value: "quoteBanner"
2614
+ }
2615
+ ]
2616
+ },
2007
2617
  {
2008
2618
  name: "title",
2009
2619
  type: "text"
@@ -2013,6 +2623,54 @@ var RichTextBlock = {
2013
2623
  type: "richText",
2014
2624
  required: true
2015
2625
  },
2626
+ {
2627
+ name: "statsItems",
2628
+ type: "array",
2629
+ fields: [
2630
+ {
2631
+ name: "value",
2632
+ type: "text",
2633
+ required: true
2634
+ },
2635
+ {
2636
+ name: "label",
2637
+ type: "text",
2638
+ required: true
2639
+ }
2640
+ ]
2641
+ },
2642
+ {
2643
+ name: "cards",
2644
+ type: "array",
2645
+ fields: [
2646
+ {
2647
+ name: "eyebrow",
2648
+ type: "text"
2649
+ },
2650
+ {
2651
+ name: "title",
2652
+ type: "text",
2653
+ required: true
2654
+ },
2655
+ {
2656
+ name: "description",
2657
+ type: "textarea"
2658
+ },
2659
+ {
2660
+ name: "media",
2661
+ type: "upload",
2662
+ relationTo: "media",
2663
+ required: false
2664
+ },
2665
+ {
2666
+ name: "imageURL",
2667
+ type: "text",
2668
+ admin: {
2669
+ description: "Optional direct image URL when this card should use an external image."
2670
+ }
2671
+ }
2672
+ ]
2673
+ },
2016
2674
  {
2017
2675
  name: "width",
2018
2676
  type: "select",
@@ -2267,18 +2925,18 @@ var sectionPresets = [
2267
2925
  blocks: [
2268
2926
  {
2269
2927
  blockType: "hero",
2270
- kicker: "Licensed + Insured",
2271
- headline: "Expert Tree Care for Central Texas",
2272
- subheadline: "Reliable trimming, safe removals, and stump grinding for residential and commercial properties.",
2273
- primaryLabel: "Get Your Free Quote",
2928
+ kicker: "Trusted Team",
2929
+ headline: "A clear headline for your primary offer",
2930
+ subheadline: "Explain what you offer, who it is for, and why it matters in one strong supporting sentence.",
2931
+ primaryLabel: "Get Started",
2274
2932
  primaryHref: "/contact",
2275
- secondaryLabel: "See Our Work",
2276
- secondaryHref: "/portfolio"
2933
+ secondaryLabel: "Learn More",
2934
+ secondaryHref: "/about"
2277
2935
  },
2278
2936
  {
2279
2937
  blockType: "cta",
2280
- headline: "Need a quote this week?",
2281
- description: "Call (512) 555-0149 or request an on-site estimate online.",
2938
+ headline: "Ready for the next step?",
2939
+ description: "Use a short call to action with a direct path to contact or purchase.",
2282
2940
  buttonLabel: "Contact Us",
2283
2941
  buttonHref: "/contact",
2284
2942
  style: "light"
@@ -2296,19 +2954,19 @@ var sectionPresets = [
2296
2954
  variant: "cards",
2297
2955
  items: [
2298
2956
  {
2299
- title: "Tree Trimming & Pruning",
2300
- description: "Canopy balancing, deadwood removal, and seasonal pruning.",
2301
- icon: "Trim"
2957
+ title: "Service One",
2958
+ description: "Briefly describe this offer or outcome.",
2959
+ icon: "01"
2302
2960
  },
2303
2961
  {
2304
- title: "Safe Tree Removal",
2305
- description: "Controlled removal for hazardous or unstable trees.",
2306
- icon: "Remove"
2962
+ title: "Service Two",
2963
+ description: "Briefly describe this offer or outcome.",
2964
+ icon: "02"
2307
2965
  },
2308
2966
  {
2309
- title: "Stump Grinding",
2310
- description: "Below-grade stump grinding and cleanup.",
2311
- icon: "Stump"
2967
+ title: "Service Three",
2968
+ description: "Briefly describe this offer or outcome.",
2969
+ icon: "03"
2312
2970
  }
2313
2971
  ]
2314
2972
  }
@@ -2324,15 +2982,15 @@ var sectionPresets = [
2324
2982
  title: "What Homeowners Say",
2325
2983
  items: [
2326
2984
  {
2327
- quote: "Great communication, fair pricing, and the cleanup was perfect. We will use them again.",
2328
- name: "Katie M.",
2329
- location: "Austin, TX",
2985
+ quote: "The experience was smooth, thoughtful, and exactly what we hoped for.",
2986
+ name: "Customer Name",
2987
+ location: "City, ST",
2330
2988
  rating: 5
2331
2989
  },
2332
2990
  {
2333
- quote: "They removed a dangerous limb over our driveway quickly and safely.",
2334
- name: "James R.",
2335
- location: "Round Rock, TX",
2991
+ quote: "Fast communication, strong service, and a result we would happily recommend.",
2992
+ name: "Customer Name",
2993
+ location: "City, ST",
2336
2994
  rating: 5
2337
2995
  }
2338
2996
  ]
@@ -2342,12 +3000,12 @@ var sectionPresets = [
2342
3000
  title: "Common Questions",
2343
3001
  items: [
2344
3002
  {
2345
- question: "How quickly can you schedule service?",
2346
- answer: "Most estimate requests are scheduled within 24 hours."
3003
+ question: "How quickly do you respond?",
3004
+ answer: "Replace this with a concise answer to a common customer question."
2347
3005
  },
2348
3006
  {
2349
- question: "Do you provide cleanup?",
2350
- answer: "Yes. Debris haul-off and cleanup are included in quoted scopes."
3007
+ question: "What is included?",
3008
+ answer: "Replace this with another concise answer that reduces buying friction."
2351
3009
  }
2352
3010
  ]
2353
3011
  }
@@ -2361,13 +3019,13 @@ var sectionPresets = [
2361
3019
  {
2362
3020
  blockType: "formEmbed",
2363
3021
  title: "Request a Quote",
2364
- description: "Share your project details and we will follow up quickly.",
3022
+ description: "Share a few details and your team can follow up with next steps.",
2365
3023
  formType: "quote"
2366
3024
  },
2367
3025
  {
2368
3026
  blockType: "bookingEmbed",
2369
3027
  title: "Prefer to book a consultation?",
2370
- description: "Choose a time window and we will confirm availability.",
3028
+ description: "Offer an alternative scheduling path for visitors who prefer to book directly.",
2371
3029
  buttonLabel: "Book Consultation",
2372
3030
  buttonHref: "/contact"
2373
3031
  }
@@ -2379,14 +3037,14 @@ var templateStarterPresets = {
2379
3037
  {
2380
3038
  blockType: "hero",
2381
3039
  headline: "Contact Us",
2382
- subheadline: "Request a quote, ask a question, or book a consultation window.",
2383
- primaryLabel: "Call (512) 555-0149",
2384
- primaryHref: "tel:+15125550149"
3040
+ subheadline: "Tell visitors exactly how to reach you and what to expect next.",
3041
+ primaryLabel: "Email Us",
3042
+ primaryHref: "mailto:hello@example.com"
2385
3043
  },
2386
3044
  {
2387
3045
  blockType: "formEmbed",
2388
3046
  title: "Request a Quote",
2389
- description: "Tell us about your project and we will follow up quickly.",
3047
+ description: "Use this space for a form embed or lead capture flow.",
2390
3048
  formType: "quote"
2391
3049
  },
2392
3050
  {
@@ -2394,8 +3052,8 @@ var templateStarterPresets = {
2394
3052
  title: "Common Questions",
2395
3053
  items: [
2396
3054
  {
2397
- question: "How quickly can you provide an estimate?",
2398
- answer: "Most estimates are scheduled within 24 hours."
3055
+ question: "How quickly will you respond?",
3056
+ answer: "Replace with the answer that best fits your operating process."
2399
3057
  }
2400
3058
  ]
2401
3059
  }
@@ -2404,27 +3062,27 @@ var templateStarterPresets = {
2404
3062
  {
2405
3063
  blockType: "hero",
2406
3064
  kicker: "Locally Owned",
2407
- headline: "Expert Tree Care for Central Texas",
2408
- subheadline: "Premium trimming, removal, and cleanup with safety-first execution.",
2409
- primaryLabel: "Get Your Free Quote",
3065
+ headline: "Lead with your strongest offer",
3066
+ subheadline: "Support the headline with a concise sentence that clarifies benefits and audience.",
3067
+ primaryLabel: "Get Started",
2410
3068
  primaryHref: "/contact",
2411
3069
  secondaryLabel: "View Services",
2412
3070
  secondaryHref: "/services"
2413
3071
  },
2414
3072
  {
2415
3073
  blockType: "featureGrid",
2416
- title: "Why Homeowners Choose Us",
3074
+ title: "Why clients choose us",
2417
3075
  variant: "highlight",
2418
3076
  items: [
2419
- { title: "Transparent Pricing", description: "Clear written estimates.", icon: "01" },
2420
- { title: "Safety-First Crew", description: "Property protection and planning.", icon: "02" },
2421
- { title: "Fast Scheduling", description: "Quick estimates and service windows.", icon: "03" }
3077
+ { title: "Clear Value", description: "Explain your first differentiator.", icon: "01" },
3078
+ { title: "Reliable Process", description: "Explain your second differentiator.", icon: "02" },
3079
+ { title: "Strong Results", description: "Explain your third differentiator.", icon: "03" }
2422
3080
  ]
2423
3081
  },
2424
3082
  {
2425
3083
  blockType: "cta",
2426
- headline: "Need a quote this week?",
2427
- description: "Call (512) 555-0149 or request an estimate online.",
3084
+ headline: "Ready to take the next step?",
3085
+ description: "Add a direct conversion prompt with a clear primary action.",
2428
3086
  buttonLabel: "Contact Us",
2429
3087
  buttonHref: "/contact",
2430
3088
  style: "light"
@@ -2433,9 +3091,9 @@ var templateStarterPresets = {
2433
3091
  services: [
2434
3092
  {
2435
3093
  blockType: "hero",
2436
- headline: "Tree Services Built for Safety and Curb Appeal",
2437
- subheadline: "Core offerings first, with clear scopes and scheduling.",
2438
- primaryLabel: "Schedule Estimate",
3094
+ headline: "Services Built Around Your Process",
3095
+ subheadline: "Summarize the core offerings with a short clarity-first introduction.",
3096
+ primaryLabel: "Request Info",
2439
3097
  primaryHref: "/contact"
2440
3098
  },
2441
3099
  {
@@ -2444,19 +3102,19 @@ var templateStarterPresets = {
2444
3102
  variant: "cards",
2445
3103
  items: [
2446
3104
  {
2447
- title: "Tree Trimming & Pruning",
2448
- description: "Selective pruning for structure, clearance, and health.",
2449
- icon: "Trim"
3105
+ title: "Service One",
3106
+ description: "Replace with a short description.",
3107
+ icon: "01"
2450
3108
  },
2451
3109
  {
2452
- title: "Tree Removal",
2453
- description: "Controlled removal for unstable or hazardous trees.",
2454
- icon: "Remove"
3110
+ title: "Service Two",
3111
+ description: "Replace with a short description.",
3112
+ icon: "02"
2455
3113
  },
2456
3114
  {
2457
- title: "Stump Grinding",
2458
- description: "Below-grade grinding and cleanup.",
2459
- icon: "Stump"
3115
+ title: "Service Three",
3116
+ description: "Replace with a short description.",
3117
+ icon: "03"
2460
3118
  }
2461
3119
  ]
2462
3120
  },
@@ -2465,8 +3123,8 @@ var templateStarterPresets = {
2465
3123
  title: "Frequently Asked Questions",
2466
3124
  items: [
2467
3125
  {
2468
- question: "Do you handle storm cleanup?",
2469
- answer: "Yes. We prioritize urgent hazards after severe weather."
3126
+ question: "Do you offer custom scopes?",
3127
+ answer: "Replace this with an answer that fits your business."
2470
3128
  }
2471
3129
  ]
2472
3130
  }
@@ -2734,7 +3392,7 @@ var withImageUploadOptimization = (collection, options = {}) => {
2734
3392
  };
2735
3393
 
2736
3394
  // src/studio/index.ts
2737
- var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3395
+ var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
2738
3396
  var makeIssue = (message, path2, code = "studio.invalid") => ({
2739
3397
  code,
2740
3398
  message,
@@ -2749,7 +3407,7 @@ var createEmptyStudioDocument = (title) => ({
2749
3407
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2750
3408
  });
2751
3409
  function assertStudioDocumentV1(input) {
2752
- if (!isRecord(input)) {
3410
+ if (!isRecord2(input)) {
2753
3411
  throw new Error("Studio document must be an object");
2754
3412
  }
2755
3413
  if (input.schemaVersion !== 1) {
@@ -2759,7 +3417,7 @@ function assertStudioDocumentV1(input) {
2759
3417
  throw new Error("Studio document nodes must be an array");
2760
3418
  }
2761
3419
  const nodes = input.nodes.map((node, index) => {
2762
- if (!isRecord(node)) {
3420
+ if (!isRecord2(node)) {
2763
3421
  throw new Error(`Node at index ${index} must be an object`);
2764
3422
  }
2765
3423
  if (typeof node.id !== "string" || node.id.length === 0) {
@@ -2768,7 +3426,7 @@ function assertStudioDocumentV1(input) {
2768
3426
  if (typeof node.type !== "string" || node.type.length === 0) {
2769
3427
  throw new Error(`Node at index ${index} has invalid type`);
2770
3428
  }
2771
- if (!isRecord(node.data)) {
3429
+ if (!isRecord2(node.data)) {
2772
3430
  throw new Error(`Node at index ${index} has invalid data`);
2773
3431
  }
2774
3432
  return {
@@ -2778,7 +3436,7 @@ function assertStudioDocumentV1(input) {
2778
3436
  };
2779
3437
  });
2780
3438
  return {
2781
- metadata: isRecord(input.metadata) ? input.metadata : void 0,
3439
+ metadata: isRecord2(input.metadata) ? input.metadata : void 0,
2782
3440
  schemaVersion: 1,
2783
3441
  title: typeof input.title === "string" ? input.title : void 0,
2784
3442
  nodes,
@@ -2856,7 +3514,7 @@ function migrateStudioDocument(value, migrations) {
2856
3514
  const sorted = [...migrations].sort((a, b) => a.fromVersion - b.fromVersion);
2857
3515
  let current = value;
2858
3516
  for (const migration of sorted) {
2859
- if (!isRecord(current) || current.schemaVersion !== migration.fromVersion) {
3517
+ if (!isRecord2(current) || current.schemaVersion !== migration.fromVersion) {
2860
3518
  continue;
2861
3519
  }
2862
3520
  current = migration.migrate(current);
@@ -2864,20 +3522,6 @@ function migrateStudioDocument(value, migrations) {
2864
3522
  return assertStudioDocumentV1(current);
2865
3523
  }
2866
3524
 
2867
- // src/studio-pages/index.ts
2868
- var studio_pages_exports = {};
2869
- __export(studio_pages_exports, {
2870
- createDefaultStudioDocument: () => createDefaultStudioDocument,
2871
- defaultBuilderThemeTokens: () => defaultBuilderThemeTokens,
2872
- layoutToStudioDocument: () => layoutToStudioDocument,
2873
- pageInspectorPanels: () => pageInspectorPanels,
2874
- pageNodeTypes: () => pageNodeTypes,
2875
- pagePaletteGroups: () => pagePaletteGroups,
2876
- pageStudioModuleManifest: () => pageStudioModuleManifest,
2877
- resolveBuilderThemeTokens: () => resolveBuilderThemeTokens,
2878
- studioDocumentToLayout: () => studioDocumentToLayout
2879
- });
2880
-
2881
3525
  // src/studio-pages/builder/settings-v2/types.ts
2882
3526
  var defaultBuilderBlockSettingsV2 = {
2883
3527
  advanced: {
@@ -2891,13 +3535,13 @@ var defaultBuilderBlockSettingsV2 = {
2891
3535
  contentGradientAngle: "135",
2892
3536
  contentGradientFrom: "#ffffff",
2893
3537
  contentGradientPreset: "none",
2894
- contentGradientTo: "#f4f6f2",
3538
+ contentGradientTo: "#f4f6f8",
2895
3539
  sectionBackgroundColor: "#ffffff",
2896
3540
  sectionBackgroundMode: "none",
2897
3541
  sectionGradientAngle: "135",
2898
- sectionGradientFrom: "#124a37",
2899
- sectionGradientPreset: "forest",
2900
- sectionGradientTo: "#1f684f"
3542
+ sectionGradientFrom: "#334b63",
3543
+ sectionGradientPreset: "slate",
3544
+ sectionGradientTo: "#496582"
2901
3545
  },
2902
3546
  layout: {
2903
3547
  contentWidth: "inherit",
@@ -2958,9 +3602,9 @@ var defaultBuilderItemSettingsV2 = {
2958
3602
  };
2959
3603
  var defaultBuilderThemeTokens = {
2960
3604
  colors: {
2961
- accent: "#0d4a37",
2962
- bodyText: "#13211c",
2963
- headingText: "#13211c",
3605
+ accent: "#334b63",
3606
+ bodyText: "#425163",
3607
+ headingText: "#182332",
2964
3608
  surface: "#ffffff"
2965
3609
  },
2966
3610
  radii: {
@@ -2978,7 +3622,7 @@ var defaultBuilderThemeTokens = {
2978
3622
  };
2979
3623
 
2980
3624
  // src/studio-pages/builder/adapters/settingsV2.ts
2981
- var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3625
+ var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
2982
3626
  var parsePercent = (value) => {
2983
3627
  if (typeof value === "number" && Number.isFinite(value)) {
2984
3628
  return Math.max(0, Math.min(100, value));
@@ -3004,12 +3648,12 @@ var parsePixel = (value) => {
3004
3648
  return null;
3005
3649
  };
3006
3650
  var mergeSettings = (defaults, input) => {
3007
- if (!isRecord2(input)) {
3651
+ if (!isRecord3(input)) {
3008
3652
  return structuredClone(defaults);
3009
3653
  }
3010
3654
  const next = structuredClone(defaults);
3011
3655
  for (const [key, value] of Object.entries(input)) {
3012
- if (isRecord2(value) && isRecord2(next[key])) {
3656
+ if (isRecord3(value) && isRecord3(next[key])) {
3013
3657
  next[key] = mergeSettings(next[key], value);
3014
3658
  continue;
3015
3659
  }
@@ -3112,7 +3756,7 @@ var v2SettingsToLegacyBlock = (blockWithSettings) => {
3112
3756
  next.imagePosition = settings.media.position;
3113
3757
  }
3114
3758
  if (Array.isArray(next.items)) {
3115
- next.items = next.items.map((rawItem) => isRecord2(rawItem) ? v2SettingsToLegacyItem(rawItem) : rawItem);
3759
+ next.items = next.items.map((rawItem) => isRecord3(rawItem) ? v2SettingsToLegacyItem(rawItem) : rawItem);
3116
3760
  }
3117
3761
  return next;
3118
3762
  };
@@ -3152,261 +3796,476 @@ var migrateBlockToSettingsV2 = (block) => {
3152
3796
  }
3153
3797
  return {
3154
3798
  ...withLegacyMirrors,
3155
- items: withLegacyMirrors.items.map((rawItem) => isRecord2(rawItem) ? v2SettingsToLegacyItem(rawItem) : rawItem)
3799
+ items: withLegacyMirrors.items.map((rawItem) => isRecord3(rawItem) ? v2SettingsToLegacyItem(rawItem) : rawItem)
3156
3800
  };
3157
3801
  };
3158
3802
 
3159
- // src/studio-pages/builder/settings-v2/themeTokens.ts
3160
- var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3161
- var merge = (base, next) => {
3162
- if (!next || !isRecord3(next)) {
3163
- return base;
3164
- }
3165
- const merged = { ...base };
3166
- for (const [key, value] of Object.entries(next)) {
3167
- if (isRecord3(value) && isRecord3(merged[key])) {
3168
- merged[key] = merge(merged[key], value);
3169
- continue;
3170
- }
3171
- if (typeof value !== "undefined") {
3172
- merged[key] = value;
3173
- }
3803
+ // src/studio-pages/document.ts
3804
+ var ensureNodeID = (value, index) => {
3805
+ if (typeof value === "string" && value.trim().length > 0) {
3806
+ return value.trim();
3174
3807
  }
3175
- return merged;
3808
+ return `node-${index + 1}`;
3176
3809
  };
3177
- var resolveBuilderThemeTokens = (layers) => {
3178
- const withSite = merge(defaultBuilderThemeTokens, layers.site);
3179
- const withPage = merge(withSite, layers.page);
3180
- return merge(withPage, layers.block);
3810
+ var layoutToStudioDocument = (layout, title, metadata) => {
3811
+ const nodes = layout.filter((block) => typeof block.blockType === "string").map((rawBlock, index) => {
3812
+ const block = migrateBlockToSettingsV2(rawBlock);
3813
+ const blockType = String(block.blockType);
3814
+ const { id, blockType: _ignoredBlockType, ...data } = block;
3815
+ return {
3816
+ id: ensureNodeID(id, index),
3817
+ type: blockType,
3818
+ data
3819
+ };
3820
+ });
3821
+ return {
3822
+ metadata,
3823
+ schemaVersion: 1,
3824
+ title,
3825
+ nodes,
3826
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3827
+ };
3181
3828
  };
3829
+ var studioDocumentToLayout = (document) => document.nodes.map(
3830
+ (node) => migrateBlockToSettingsV2({
3831
+ id: node.id,
3832
+ blockType: node.type,
3833
+ ...node.data
3834
+ })
3835
+ );
3836
+ var createDefaultStudioDocument = (title) => ({
3837
+ metadata: {},
3838
+ schemaVersion: 1,
3839
+ title,
3840
+ nodes: [],
3841
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3842
+ });
3182
3843
 
3183
- // src/studio-pages/builder/settings-v2/BlockInspectorRenderer.tsx
3184
- var import_react2 = require("react");
3185
-
3186
- // src/studio-pages/builder/ui/Accordion.tsx
3187
- var import_react = require("react");
3188
- var import_jsx_runtime4 = require("react/jsx-runtime");
3189
-
3190
- // src/studio-pages/builder/ui/ImageControls.tsx
3191
- var import_jsx_runtime5 = require("react/jsx-runtime");
3192
-
3193
- // src/studio-pages/builder/settings-v2/inspectorSchema.ts
3194
- var alignOptions = [
3195
- { label: "Left", value: "left" },
3196
- { label: "Center", value: "center" },
3197
- { label: "Right", value: "right" },
3198
- { label: "Justify", value: "justify" }
3199
- ];
3200
- var layoutFieldSet = [
3201
- {
3202
- group: "layout",
3203
- key: "settings.layout.contentWidth",
3204
- label: "Content Width",
3205
- options: [
3206
- { label: "Inherit Page Default", value: "inherit" },
3207
- { label: "Narrow", value: "narrow" },
3208
- { label: "Content", value: "content" },
3209
- { label: "Wide", value: "wide" },
3210
- { label: "Full", value: "full" }
3211
- ],
3212
- tags: ["width", "container"],
3213
- type: "select"
3214
- },
3215
- {
3216
- group: "layout",
3217
- key: "settings.layout.linkVerticalPadding",
3218
- label: "Keep Top and Bottom Equal",
3219
- tags: ["spacing", "padding", "equal", "lock", "vertical"],
3220
- type: "checkbox"
3221
- },
3222
- {
3223
- group: "layout",
3224
- key: "settings.layout.paddingTopPt",
3225
- label: "Top Padding (pt)",
3226
- max: 240,
3227
- min: 0,
3228
- tags: ["spacing", "padding", "top", "vertical"],
3229
- type: "number"
3230
- },
3231
- {
3232
- group: "layout",
3233
- key: "settings.layout.paddingBottomPt",
3234
- label: "Bottom Padding (pt)",
3235
- max: 240,
3236
- min: 0,
3237
- tags: ["spacing", "padding", "bottom", "vertical"],
3238
- type: "number"
3239
- },
3240
- {
3241
- group: "layout",
3242
- key: "settings.layout.linkHorizontalPadding",
3243
- label: "Keep Left and Right Equal",
3244
- tags: ["spacing", "padding", "equal", "lock", "horizontal"],
3245
- type: "checkbox"
3246
- },
3247
- {
3248
- group: "layout",
3249
- key: "settings.layout.paddingLeftPt",
3250
- label: "Left Padding (pt)",
3251
- max: 240,
3252
- min: 0,
3253
- tags: ["spacing", "padding", "left", "horizontal"],
3254
- type: "number"
3255
- },
3256
- {
3257
- group: "layout",
3258
- key: "settings.layout.paddingRightPt",
3259
- label: "Right Padding (pt)",
3260
- max: 240,
3261
- min: 0,
3262
- tags: ["spacing", "padding", "right", "horizontal"],
3263
- type: "number"
3264
- }
3265
- ];
3266
- var typographyFieldSet = [
3267
- {
3268
- group: "typography",
3269
- key: "settings.typography.headingAlign",
3270
- label: "Heading Alignment",
3271
- options: alignOptions,
3272
- tags: ["text", "align", "heading"],
3273
- type: "select"
3274
- },
3275
- {
3276
- group: "typography",
3277
- key: "settings.typography.bodyAlign",
3278
- label: "Body Alignment",
3279
- options: alignOptions,
3280
- tags: ["text", "align", "paragraph"],
3281
- type: "select"
3282
- },
3283
- {
3284
- group: "typography",
3285
- key: "settings.typography.maxTextWidth",
3286
- label: "Text Width",
3287
- options: [
3288
- { label: "Auto", value: "auto" },
3289
- { label: "Small", value: "sm" },
3290
- { label: "Medium", value: "md" },
3291
- { label: "Large", value: "lg" },
3292
- { label: "Full", value: "full" }
3293
- ],
3294
- tags: ["readability", "measure", "line length"],
3295
- type: "select"
3296
- },
3297
- {
3298
- advanced: true,
3299
- group: "typography",
3300
- key: "settings.typography.lineHeightPreset",
3301
- label: "Line Height",
3302
- options: [
3303
- { label: "Tight", value: "tight" },
3304
- { label: "Normal", value: "normal" },
3305
- { label: "Relaxed", value: "relaxed" }
3306
- ],
3307
- type: "select"
3308
- },
3309
- {
3310
- advanced: true,
3311
- group: "typography",
3312
- key: "settings.typography.letterSpacingPreset",
3313
- label: "Letter Spacing",
3314
- options: [
3315
- { label: "Tight", value: "tight" },
3316
- { label: "Normal", value: "normal" },
3317
- { label: "Relaxed", value: "relaxed" }
3318
- ],
3319
- type: "select"
3844
+ // src/nextjs/queries/pages.ts
3845
+ var PAGE_QUERY_CACHE_VERSION = "v4-published-only-public";
3846
+ function withStudioDocumentLayout(page) {
3847
+ if (!page) {
3848
+ return null;
3320
3849
  }
3321
- ];
3322
- var styleFieldSet = [
3323
- {
3324
- group: "style",
3325
- key: "settings.appearance.sectionBackgroundMode",
3326
- label: "Section Background",
3327
- options: [
3328
- { label: "None", value: "none" },
3329
- { label: "Color", value: "color" },
3330
- { label: "Gradient", value: "gradient" }
3331
- ],
3332
- tags: ["background", "section"],
3333
- type: "select"
3334
- },
3335
- {
3336
- group: "style",
3337
- key: "settings.appearance.sectionBackgroundColor",
3338
- label: "Section Background Color",
3339
- type: "color"
3340
- },
3341
- {
3342
- group: "style",
3343
- key: "settings.appearance.contentBackgroundMode",
3344
- label: "Content Background",
3345
- options: [
3346
- { label: "None", value: "none" },
3347
- { label: "Color", value: "color" },
3348
- { label: "Gradient", value: "gradient" }
3349
- ],
3350
- tags: ["background", "content"],
3351
- type: "select"
3352
- },
3353
- {
3354
- group: "style",
3355
- key: "settings.appearance.contentBackgroundColor",
3356
- label: "Content Background Color",
3357
- type: "color"
3850
+ try {
3851
+ const studioDocument = assertStudioDocumentV1(page.studioDocument);
3852
+ const compiledLayout = studioDocumentToLayout(studioDocument);
3853
+ if (Array.isArray(compiledLayout) && compiledLayout.length > 0) {
3854
+ return {
3855
+ ...page,
3856
+ layout: compiledLayout
3857
+ };
3858
+ }
3859
+ } catch {
3358
3860
  }
3359
- ];
3360
- var commonAdvanced = [
3361
- {
3362
- group: "advanced",
3363
- inlineEditable: false,
3364
- key: "settings.advanced.editCopyInPanel",
3365
- label: "Edit Copy In Panel",
3366
- tags: ["inline", "copy", "text"],
3367
- type: "checkbox"
3368
- },
3369
- {
3370
- advanced: true,
3371
- group: "advanced",
3372
- key: "settings.advanced.hideOnMobile",
3373
- label: "Hide On Mobile",
3374
- type: "checkbox"
3375
- },
3376
- {
3377
- advanced: true,
3378
- group: "advanced",
3379
- key: "settings.advanced.customClassName",
3380
- label: "Custom Class Name",
3381
- type: "text"
3861
+ return page;
3862
+ }
3863
+ function normalizePath(segments) {
3864
+ if (!segments || segments.length === 0) {
3865
+ return "/";
3382
3866
  }
3383
- ];
3384
- var mediaFieldSet = [
3385
- {
3386
- group: "media",
3387
- key: "settings.media.fit",
3388
- label: "Image Fit",
3389
- options: [
3390
- { label: "Cover", value: "cover" },
3391
- { label: "Contain", value: "contain" }
3392
- ],
3393
- tags: ["image", "media"],
3394
- type: "select"
3395
- },
3396
- {
3397
- group: "media",
3398
- key: "settings.media.cornerStyle",
3399
- label: "Image Corners",
3400
- options: [
3401
- { label: "Rounded", value: "rounded" },
3402
- { label: "Square", value: "square" }
3403
- ],
3404
- tags: ["image", "radius", "corners"],
3405
- type: "select"
3867
+ const cleaned = segments.map((segment) => segment.trim()).filter(Boolean).join("/");
3868
+ return cleaned.length > 0 ? `/${cleaned}` : "/";
3869
+ }
3870
+ async function queryPageByPath(payload, path2, draft) {
3871
+ const pathWhere = {
3872
+ path: {
3873
+ equals: path2
3874
+ }
3875
+ };
3876
+ const publishedWhere = {
3877
+ _status: {
3878
+ equals: "published"
3879
+ }
3880
+ };
3881
+ const result = await payload.find({
3882
+ collection: "pages",
3883
+ depth: 2,
3884
+ draft,
3885
+ limit: 1,
3886
+ overrideAccess: false,
3887
+ where: draft ? pathWhere : { and: [pathWhere, publishedWhere] }
3888
+ });
3889
+ if (result.docs.length > 0) {
3890
+ return withStudioDocumentLayout(result.docs[0] || null);
3406
3891
  }
3407
- ];
3408
- var inspectorDefinitionByBlockType = {
3409
- beforeAfter: {
3892
+ if (path2 === "/") {
3893
+ const homeWhere = {
3894
+ slug: {
3895
+ equals: "home"
3896
+ }
3897
+ };
3898
+ const homeResult = await payload.find({
3899
+ collection: "pages",
3900
+ depth: 2,
3901
+ draft,
3902
+ limit: 1,
3903
+ overrideAccess: false,
3904
+ where: draft ? homeWhere : { and: [homeWhere, publishedWhere] }
3905
+ });
3906
+ return withStudioDocumentLayout(homeResult.docs[0] || null);
3907
+ }
3908
+ return null;
3909
+ }
3910
+ function createPageQueries(getPayloadClient, contentTag = "website-content") {
3911
+ const getPublishedPageByPathCached = (0, import_cache.unstable_cache)(
3912
+ async (path2) => {
3913
+ const payload = await getPayloadClient();
3914
+ return queryPageByPath(payload, path2, false);
3915
+ },
3916
+ ["page-by-path", PAGE_QUERY_CACHE_VERSION],
3917
+ { tags: [contentTag] }
3918
+ );
3919
+ async function getPageBySegments(segments, draft = false) {
3920
+ const path2 = normalizePath(segments);
3921
+ const payload = await getPayloadClient();
3922
+ if (draft) {
3923
+ return queryPageByPath(payload, path2, true);
3924
+ }
3925
+ return getPublishedPageByPathCached(path2);
3926
+ }
3927
+ async function listPublishedPagePaths() {
3928
+ const payload = await getPayloadClient();
3929
+ const pages = await payload.find({
3930
+ collection: "pages",
3931
+ depth: 0,
3932
+ draft: false,
3933
+ limit: 1e3,
3934
+ pagination: false,
3935
+ overrideAccess: false,
3936
+ where: {
3937
+ _status: {
3938
+ equals: "published"
3939
+ }
3940
+ }
3941
+ });
3942
+ return pages.docs.map((doc) => doc.path).filter((path2) => typeof path2 === "string" && path2.length > 0);
3943
+ }
3944
+ function pathToSegments(path2) {
3945
+ if (!path2 || path2 === "/") {
3946
+ return [];
3947
+ }
3948
+ return path2.split("/").filter(Boolean);
3949
+ }
3950
+ return {
3951
+ getPageBySegments,
3952
+ listPublishedPagePaths,
3953
+ pathToSegments
3954
+ };
3955
+ }
3956
+
3957
+ // src/nextjs/queries/site.ts
3958
+ var import_cache2 = require("next/cache");
3959
+ function createSiteQueries(getPayloadClient, contentTag = "website-content") {
3960
+ const getSiteSettingsCached = (0, import_cache2.unstable_cache)(
3961
+ async () => {
3962
+ const payload = await getPayloadClient();
3963
+ const settings = await payload.findGlobal({
3964
+ slug: "site-settings",
3965
+ depth: 1
3966
+ });
3967
+ return settings;
3968
+ },
3969
+ ["site-settings-global"],
3970
+ { tags: [contentTag] }
3971
+ );
3972
+ const getHeaderCached = (0, import_cache2.unstable_cache)(
3973
+ async () => {
3974
+ const payload = await getPayloadClient();
3975
+ const header = await payload.findGlobal({
3976
+ slug: "header",
3977
+ depth: 1
3978
+ });
3979
+ return header;
3980
+ },
3981
+ ["header-global"],
3982
+ { tags: [contentTag] }
3983
+ );
3984
+ const getFooterCached = (0, import_cache2.unstable_cache)(
3985
+ async () => {
3986
+ const payload = await getPayloadClient();
3987
+ const footer = await payload.findGlobal({
3988
+ slug: "footer",
3989
+ depth: 1
3990
+ });
3991
+ return footer;
3992
+ },
3993
+ ["footer-global"],
3994
+ { tags: [contentTag] }
3995
+ );
3996
+ const getSocialMediaCached = (0, import_cache2.unstable_cache)(
3997
+ async () => {
3998
+ const payload = await getPayloadClient();
3999
+ const socialMedia = await payload.findGlobal({
4000
+ slug: "social-media",
4001
+ depth: 1
4002
+ });
4003
+ return socialMedia;
4004
+ },
4005
+ ["social-media-global"],
4006
+ { tags: [contentTag] }
4007
+ );
4008
+ async function getSiteSettings(draft = false) {
4009
+ if (draft) {
4010
+ const payload = await getPayloadClient();
4011
+ const settings = await payload.findGlobal({
4012
+ slug: "site-settings",
4013
+ depth: 1,
4014
+ draft: true
4015
+ });
4016
+ return settings;
4017
+ }
4018
+ return getSiteSettingsCached();
4019
+ }
4020
+ async function getHeader(draft = false) {
4021
+ if (draft) {
4022
+ const payload = await getPayloadClient();
4023
+ const header = await payload.findGlobal({
4024
+ slug: "header",
4025
+ depth: 1,
4026
+ draft: true
4027
+ });
4028
+ return header;
4029
+ }
4030
+ return getHeaderCached();
4031
+ }
4032
+ async function getFooter(draft = false) {
4033
+ if (draft) {
4034
+ const payload = await getPayloadClient();
4035
+ const footer = await payload.findGlobal({
4036
+ slug: "footer",
4037
+ depth: 1,
4038
+ draft: true
4039
+ });
4040
+ return footer;
4041
+ }
4042
+ return getFooterCached();
4043
+ }
4044
+ async function getSocialMedia(draft = false) {
4045
+ if (draft) {
4046
+ const payload = await getPayloadClient();
4047
+ const socialMedia = await payload.findGlobal({
4048
+ slug: "social-media",
4049
+ depth: 1,
4050
+ draft: true
4051
+ });
4052
+ return socialMedia;
4053
+ }
4054
+ return getSocialMediaCached();
4055
+ }
4056
+ return {
4057
+ getSiteSettings,
4058
+ getHeader,
4059
+ getFooter,
4060
+ getSocialMedia
4061
+ };
4062
+ }
4063
+
4064
+ // src/nextjs/utilities/media.ts
4065
+ function resolveMedia(media) {
4066
+ if (!media) {
4067
+ return null;
4068
+ }
4069
+ if (typeof media === "number" || typeof media === "string") {
4070
+ return null;
4071
+ }
4072
+ if (media.url) {
4073
+ return {
4074
+ url: media.url,
4075
+ alt: media.alt || ""
4076
+ };
4077
+ }
4078
+ if (media.filename) {
4079
+ return {
4080
+ url: `/media/${media.filename}`,
4081
+ alt: media.alt || ""
4082
+ };
4083
+ }
4084
+ return null;
4085
+ }
4086
+
4087
+ // src/nextjs/utilities/socialMedia.ts
4088
+ function resolveSocialMediaLinks(data) {
4089
+ const profiles = data?.profiles;
4090
+ if (!profiles || typeof profiles !== "object") {
4091
+ return [];
4092
+ }
4093
+ return SOCIAL_MEDIA_PLATFORMS.reduce((acc, platform) => {
4094
+ const profile = profiles[platform];
4095
+ if (!profile || typeof profile !== "object") {
4096
+ return acc;
4097
+ }
4098
+ const url = typeof profile.url === "string" ? profile.url.trim() : "";
4099
+ if (!url) {
4100
+ return acc;
4101
+ }
4102
+ const icon = typeof profile.icon === "string" && profile.icon.trim().length > 0 ? profile.icon.trim() : SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM[platform];
4103
+ acc.push({
4104
+ icon,
4105
+ label: SOCIAL_MEDIA_PLATFORM_LABELS[platform],
4106
+ platform,
4107
+ url
4108
+ });
4109
+ return acc;
4110
+ }, []);
4111
+ }
4112
+
4113
+ // src/studio-pages/index.ts
4114
+ var studio_pages_exports = {};
4115
+ __export(studio_pages_exports, {
4116
+ createDefaultStudioDocument: () => createDefaultStudioDocument,
4117
+ createStudioPageService: () => createStudioPageService,
4118
+ defaultBuilderThemeTokens: () => defaultBuilderThemeTokens,
4119
+ getStudioDocumentFromPage: () => getStudioDocumentFromPage,
4120
+ layoutToStudioDocument: () => layoutToStudioDocument,
4121
+ pageInspectorPanels: () => pageInspectorPanels,
4122
+ pageNodeTypes: () => pageNodeTypes,
4123
+ pagePaletteGroups: () => pagePaletteGroups,
4124
+ pageStudioModuleManifest: () => pageStudioModuleManifest,
4125
+ resolveBuilderThemeTokens: () => resolveBuilderThemeTokens,
4126
+ studioDocumentToLayout: () => studioDocumentToLayout,
4127
+ toEditorInitialDoc: () => toEditorInitialDoc
4128
+ });
4129
+
4130
+ // src/studio-pages/builder/settings-v2/themeTokens.ts
4131
+ var isRecord4 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
4132
+ var merge = (base, next) => {
4133
+ if (!next || !isRecord4(next)) {
4134
+ return base;
4135
+ }
4136
+ const merged = { ...base };
4137
+ for (const [key, value] of Object.entries(next)) {
4138
+ if (isRecord4(value) && isRecord4(merged[key])) {
4139
+ merged[key] = merge(merged[key], value);
4140
+ continue;
4141
+ }
4142
+ if (typeof value !== "undefined") {
4143
+ merged[key] = value;
4144
+ }
4145
+ }
4146
+ return merged;
4147
+ };
4148
+ var resolveBuilderThemeTokens = (layers) => {
4149
+ const withSite = merge(defaultBuilderThemeTokens, layers.site);
4150
+ const withPage = merge(withSite, layers.page);
4151
+ return merge(withPage, layers.block);
4152
+ };
4153
+
4154
+ // src/studio-pages/builder/settings-v2/inspectorSchema.ts
4155
+ var alignOptions = [
4156
+ { label: "Left", value: "left" },
4157
+ { label: "Center", value: "center" },
4158
+ { label: "Right", value: "right" },
4159
+ { label: "Justify", value: "justify" }
4160
+ ];
4161
+ var layoutFieldSet = [];
4162
+ var typographyFieldSet = [
4163
+ {
4164
+ group: "typography",
4165
+ key: "settings.typography.headingAlign",
4166
+ label: "Heading Alignment",
4167
+ options: alignOptions,
4168
+ tags: ["text", "align", "heading"],
4169
+ type: "select"
4170
+ },
4171
+ {
4172
+ group: "typography",
4173
+ key: "settings.typography.bodyAlign",
4174
+ label: "Body Alignment",
4175
+ options: alignOptions,
4176
+ tags: ["text", "align", "paragraph"],
4177
+ type: "select"
4178
+ },
4179
+ {
4180
+ group: "typography",
4181
+ key: "settings.typography.maxTextWidth",
4182
+ label: "Text Width",
4183
+ options: [
4184
+ { label: "Auto", value: "auto" },
4185
+ { label: "Small", value: "sm" },
4186
+ { label: "Medium", value: "md" },
4187
+ { label: "Large", value: "lg" },
4188
+ { label: "Full", value: "full" }
4189
+ ],
4190
+ tags: ["readability", "measure", "line length"],
4191
+ type: "select"
4192
+ },
4193
+ {
4194
+ advanced: true,
4195
+ group: "typography",
4196
+ key: "settings.typography.lineHeightPreset",
4197
+ label: "Line Height",
4198
+ options: [
4199
+ { label: "Tight", value: "tight" },
4200
+ { label: "Normal", value: "normal" },
4201
+ { label: "Relaxed", value: "relaxed" }
4202
+ ],
4203
+ type: "select"
4204
+ },
4205
+ {
4206
+ advanced: true,
4207
+ group: "typography",
4208
+ key: "settings.typography.letterSpacingPreset",
4209
+ label: "Letter Spacing",
4210
+ options: [
4211
+ { label: "Tight", value: "tight" },
4212
+ { label: "Normal", value: "normal" },
4213
+ { label: "Relaxed", value: "relaxed" }
4214
+ ],
4215
+ type: "select"
4216
+ }
4217
+ ];
4218
+ var styleFieldSet = [];
4219
+ var commonAdvanced = [
4220
+ {
4221
+ group: "advanced",
4222
+ inlineEditable: false,
4223
+ key: "settings.advanced.editCopyInPanel",
4224
+ label: "Edit Copy In Panel",
4225
+ tags: ["inline", "copy", "text"],
4226
+ type: "checkbox"
4227
+ },
4228
+ {
4229
+ advanced: true,
4230
+ group: "advanced",
4231
+ key: "settings.advanced.hideOnMobile",
4232
+ label: "Hide On Mobile",
4233
+ type: "checkbox"
4234
+ },
4235
+ {
4236
+ advanced: true,
4237
+ group: "advanced",
4238
+ key: "settings.advanced.customClassName",
4239
+ label: "Custom Class Name",
4240
+ type: "text"
4241
+ }
4242
+ ];
4243
+ var mediaFieldSet = [
4244
+ {
4245
+ group: "media",
4246
+ key: "settings.media.fit",
4247
+ label: "Image Fit",
4248
+ options: [
4249
+ { label: "Cover", value: "cover" },
4250
+ { label: "Contain", value: "contain" }
4251
+ ],
4252
+ tags: ["image", "media"],
4253
+ type: "select"
4254
+ },
4255
+ {
4256
+ group: "media",
4257
+ key: "settings.media.cornerStyle",
4258
+ label: "Image Corners",
4259
+ options: [
4260
+ { label: "Rounded", value: "rounded" },
4261
+ { label: "Square", value: "square" }
4262
+ ],
4263
+ tags: ["image", "radius", "corners"],
4264
+ type: "select"
4265
+ }
4266
+ ];
4267
+ var inspectorDefinitionByBlockType = {
4268
+ beforeAfter: {
3410
4269
  blockType: "beforeAfter",
3411
4270
  fields: [
3412
4271
  { group: "basics", inlineEditable: true, key: "title", label: "Title", type: "text" },
@@ -3457,6 +4316,7 @@ var inspectorDefinitionByBlockType = {
3457
4316
  faq: {
3458
4317
  blockType: "faq",
3459
4318
  fields: [
4319
+ { group: "basics", inlineEditable: true, key: "eyebrow", label: "Eyebrow", type: "text" },
3460
4320
  { group: "basics", inlineEditable: true, key: "title", label: "Title", type: "text" },
3461
4321
  ...layoutFieldSet,
3462
4322
  ...typographyFieldSet,
@@ -3467,6 +4327,7 @@ var inspectorDefinitionByBlockType = {
3467
4327
  featureGrid: {
3468
4328
  blockType: "featureGrid",
3469
4329
  fields: [
4330
+ { group: "basics", inlineEditable: true, key: "eyebrow", label: "Eyebrow", type: "text" },
3470
4331
  { group: "basics", inlineEditable: true, key: "title", label: "Title", type: "text" },
3471
4332
  {
3472
4333
  group: "basics",
@@ -3474,7 +4335,11 @@ var inspectorDefinitionByBlockType = {
3474
4335
  label: "Variant",
3475
4336
  options: [
3476
4337
  { label: "Cards", value: "cards" },
3477
- { label: "Highlight", value: "highlight" }
4338
+ { label: "Highlight", value: "highlight" },
4339
+ { label: "Split List", value: "splitList" },
4340
+ { label: "Panels", value: "panels" },
4341
+ { label: "Catalog", value: "catalog" },
4342
+ { label: "Contact Split", value: "contact" }
3478
4343
  ],
3479
4344
  type: "select"
3480
4345
  },
@@ -3489,6 +4354,7 @@ var inspectorDefinitionByBlockType = {
3489
4354
  formEmbed: {
3490
4355
  blockType: "formEmbed",
3491
4356
  fields: [
4357
+ { group: "basics", inlineEditable: true, key: "eyebrow", label: "Eyebrow", type: "text" },
3492
4358
  { group: "basics", inlineEditable: true, key: "title", label: "Title", type: "text" },
3493
4359
  { group: "basics", inlineEditable: true, key: "description", label: "Description", type: "textarea" },
3494
4360
  {
@@ -3521,7 +4387,7 @@ var inspectorDefinitionByBlockType = {
3521
4387
  type: "select"
3522
4388
  },
3523
4389
  {
3524
- group: "layout",
4390
+ group: "basics",
3525
4391
  key: "heroHeight",
3526
4392
  label: "Hero Height",
3527
4393
  options: [
@@ -3574,6 +4440,16 @@ var inspectorDefinitionByBlockType = {
3574
4440
  richText: {
3575
4441
  blockType: "richText",
3576
4442
  fields: [
4443
+ {
4444
+ group: "basics",
4445
+ key: "variant",
4446
+ label: "Variant",
4447
+ options: [
4448
+ { label: "Default", value: "default" },
4449
+ { label: "Quote Banner", value: "quoteBanner" }
4450
+ ],
4451
+ type: "select"
4452
+ },
3577
4453
  { group: "basics", inlineEditable: true, key: "title", label: "Title", type: "text" },
3578
4454
  {
3579
4455
  group: "basics",
@@ -3607,679 +4483,573 @@ var inspectorDefinitionByBlockType = {
3607
4483
  fields: [
3608
4484
  { group: "basics", inlineEditable: true, key: "title", label: "Title", type: "text" },
3609
4485
  { group: "basics", key: "visibleCount", label: "Visible At Once", max: 6, min: 1, type: "number" },
3610
- { group: "basics", key: "autoRotate", label: "Auto Rotate", type: "checkbox" },
3611
- {
3612
- advanced: true,
3613
- group: "advanced",
3614
- key: "rotateIntervalSeconds",
3615
- label: "Rotate Interval Seconds",
3616
- max: 30,
3617
- min: 2,
3618
- type: "number"
3619
- },
3620
- ...layoutFieldSet,
3621
- ...typographyFieldSet,
3622
- ...styleFieldSet,
3623
- ...commonAdvanced
3624
- ]
3625
- }
3626
- };
3627
-
3628
- // src/studio-pages/builder/settings-v2/BlockInspectorRenderer.tsx
3629
- var import_jsx_runtime6 = require("react/jsx-runtime");
3630
-
3631
- // src/studio-pages/migrations.ts
3632
- var isRecord4 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
3633
- var assertPageStudioDocumentV1 = (value) => {
3634
- if (!isRecord4(value)) {
3635
- throw new Error("Studio document must be an object");
3636
- }
3637
- if (value.schemaVersion !== 1) {
3638
- throw new Error("Unsupported studio schemaVersion");
3639
- }
3640
- if (!Array.isArray(value.nodes)) {
3641
- throw new Error("Studio document nodes must be an array");
3642
- }
3643
- const nodes = value.nodes.map((node, index) => {
3644
- if (!isRecord4(node)) {
3645
- throw new Error(`Node at index ${index} must be an object`);
3646
- }
3647
- if (typeof node.id !== "string" || node.id.length === 0) {
3648
- throw new Error(`Node at index ${index} has invalid id`);
3649
- }
3650
- if (typeof node.type !== "string" || node.type.length === 0) {
3651
- throw new Error(`Node at index ${index} has invalid type`);
3652
- }
3653
- if (!isRecord4(node.data)) {
3654
- throw new Error(`Node at index ${index} has invalid data`);
3655
- }
3656
- return {
3657
- data: node.data,
3658
- id: node.id,
3659
- type: node.type
3660
- };
3661
- });
3662
- return {
3663
- metadata: isRecord4(value.metadata) ? value.metadata : void 0,
3664
- nodes,
3665
- schemaVersion: 1,
3666
- title: typeof value.title === "string" ? value.title : void 0,
3667
- updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : void 0
3668
- };
3669
- };
3670
- var migratePageNodeToSettingsV2 = (node) => {
3671
- const normalized = migrateBlockToSettingsV2({
3672
- blockType: node.type,
3673
- ...node.data
3674
- });
3675
- const { blockType: _ignoredBlockType, ...data } = normalized;
3676
- return {
3677
- ...node,
3678
- data
3679
- };
3680
- };
3681
- var migratePageDocumentSettingsToV2 = (value) => {
3682
- const current = assertPageStudioDocumentV1(value);
3683
- return {
3684
- ...current,
3685
- nodes: current.nodes.map(migratePageNodeToSettingsV2)
3686
- };
3687
- };
3688
- var pageStudioMigrations = [
3689
- {
3690
- fromVersion: 1,
3691
- migrate: migratePageDocumentSettingsToV2,
3692
- toVersion: 1
3693
- }
3694
- ];
3695
-
3696
- // src/studio-pages/index.ts
3697
- var withSectionStyleDefaults = (value) => ({
3698
- ...sectionStyleDefaults,
3699
- ...value
3700
- });
3701
- var defaultNodeData = {
3702
- bookingEmbed: {
3703
- ...withSectionStyleDefaults({}),
3704
- buttonHref: "/contact",
3705
- buttonLabel: "Book Consultation",
3706
- description: "Let visitors book a consultation.",
3707
- title: "Book a Time"
3708
- },
3709
- beforeAfter: withSectionStyleDefaults({
3710
- itemsPerRow: 2,
3711
- items: [
3712
- {
3713
- description: "Before and after result summary.",
3714
- imageCornerStyle: "rounded",
3715
- imageFit: "cover",
3716
- imagePosition: "center",
3717
- label: "Project One"
3718
- }
3719
- ],
3720
- subtitle: "Show visual proof from real projects.",
3721
- title: "Before & After Results"
3722
- }),
3723
- cta: {
3724
- ...withSectionStyleDefaults({}),
3725
- backgroundColor: "#1f684f",
3726
- buttonHref: "/contact",
3727
- buttonLabel: "Contact Us",
3728
- description: "Optional supporting copy.",
3729
- headline: "Ready to get started?",
3730
- style: "light"
3731
- },
3732
- faq: {
3733
- ...withSectionStyleDefaults({}),
3734
- items: [{ answer: "Answer goes here.", question: "Frequently asked question?" }],
3735
- title: "Frequently Asked Questions"
3736
- },
3737
- featureGrid: {
3738
- ...withSectionStyleDefaults({}),
3739
- itemsPerRow: 3,
3740
- items: [
3741
- { description: "Explain this point.", iconType: "badge", icon: "01", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature One" },
3742
- { description: "Explain this point.", iconType: "badge", icon: "02", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature Two" },
3743
- { description: "Explain this point.", iconType: "badge", icon: "03", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature Three" }
3744
- ],
3745
- title: "Section Title",
3746
- variant: "cards"
3747
- },
3748
- formEmbed: {
3749
- ...withSectionStyleDefaults({}),
3750
- description: "Collect lead details from visitors.",
3751
- formType: "quote",
3752
- title: "Request a Quote"
3753
- },
3754
- hero: {
3755
- ...withSectionStyleDefaults({}),
3756
- backgroundColor: "",
3757
- backgroundOverlayMode: "none",
3758
- backgroundOverlayOpacity: 45,
3759
- backgroundOverlayColor: "#000000",
3760
- backgroundOverlayGradientFrom: "#0d4a37",
3761
- backgroundOverlayGradientTo: "#1f684f",
3762
- backgroundOverlayGradientAngle: "135",
3763
- backgroundOverlayGradientFromStrength: 100,
3764
- backgroundOverlayGradientToStrength: 100,
3765
- backgroundOverlayGradientStart: 0,
3766
- backgroundOverlayGradientEnd: 100,
3767
- backgroundOverlayGradientFeather: 100,
3768
- backgroundImageCornerStyle: "rounded",
3769
- backgroundImageFit: "cover",
3770
- backgroundImagePosition: "center",
3771
- heroHeight: "sm",
3772
- headline: "New Hero Section",
3773
- kicker: "Optional kicker",
3774
- primaryHref: "/contact",
3775
- primaryLabel: "Primary Action",
3776
- secondaryHref: "/services",
3777
- secondaryLabel: "Secondary Action",
3778
- subheadline: "Describe your offer clearly for website visitors.",
3779
- variant: "default"
3780
- },
3781
- media: {
3782
- ...withSectionStyleDefaults({}),
3783
- caption: "Add a caption",
3784
- imageCornerStyle: "rounded",
3785
- imageFit: "cover",
3786
- imagePosition: "center",
3787
- size: "default"
3788
- },
3789
- logoWall: withSectionStyleDefaults({
3790
- items: [
3791
- { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 1" },
3792
- { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 2" },
3793
- { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 3" }
3794
- ],
3795
- subtitle: "Trusted by teams and homeowners across Central Texas.",
3796
- title: "Trusted by Local Organizations"
3797
- }),
3798
- richText: {
3799
- ...withSectionStyleDefaults({}),
3800
- content: {
3801
- root: {
3802
- children: [
3803
- {
3804
- children: [
3805
- {
3806
- detail: 0,
3807
- format: 0,
3808
- mode: "normal",
3809
- style: "",
3810
- text: "Write your content here.",
3811
- type: "text",
3812
- version: 1
3813
- }
3814
- ],
3815
- direction: "ltr",
3816
- format: "",
3817
- indent: 0,
3818
- type: "paragraph",
3819
- version: 1
3820
- }
3821
- ],
3822
- direction: "ltr",
3823
- format: "",
3824
- indent: 0,
3825
- type: "root",
3826
- version: 1
3827
- }
3828
- },
3829
- title: "Section Heading",
3830
- width: "normal"
3831
- },
3832
- testimonials: {
3833
- ...withSectionStyleDefaults({}),
3834
- autoRotate: true,
3835
- items: [{ location: "City, ST", name: "Customer Name", quote: "Customer feedback goes here.", rating: 5 }],
3836
- rotateIntervalSeconds: 7,
3837
- title: "What Customers Say",
3838
- visibleCount: 3
3839
- },
3840
- stats: withSectionStyleDefaults({
3841
- items: [
3842
- { description: "Average response time", label: "Same-Day Quotes", value: "24h" },
3843
- { description: "Projects completed", label: "Completed Jobs", value: "1,200+" },
3844
- { description: "Client satisfaction score", label: "Satisfaction", value: "4.9/5" }
3845
- ],
3846
- subtitle: "Highlight measurable outcomes to build trust quickly.",
3847
- title: "Performance Highlights"
3848
- })
4486
+ { group: "basics", key: "autoRotate", label: "Auto Rotate", type: "checkbox" },
4487
+ {
4488
+ advanced: true,
4489
+ group: "advanced",
4490
+ key: "rotateIntervalSeconds",
4491
+ label: "Rotate Interval Seconds",
4492
+ max: 30,
4493
+ min: 2,
4494
+ type: "number"
4495
+ },
4496
+ ...layoutFieldSet,
4497
+ ...typographyFieldSet,
4498
+ ...styleFieldSet,
4499
+ ...commonAdvanced
4500
+ ]
4501
+ }
3849
4502
  };
3850
- var nodeTypeLabels = {
3851
- bookingEmbed: "Booking Embed",
3852
- beforeAfter: "Before / After",
3853
- cta: "Call To Action",
3854
- faq: "FAQ",
3855
- featureGrid: "Feature Grid",
3856
- formEmbed: "Form Embed",
3857
- hero: "Hero",
3858
- logoWall: "Logo Wall",
3859
- media: "Media",
3860
- richText: "Rich Text",
3861
- stats: "Stats",
3862
- testimonials: "Testimonials"
4503
+
4504
+ // src/studio-pages/pageService.ts
4505
+ var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
4506
+ var toRecordArray = (value) => Array.isArray(value) ? value.filter((item) => isRecord5(item)) : [];
4507
+ var getRelationID = (value) => {
4508
+ if (typeof value === "number" || typeof value === "string") {
4509
+ return value;
4510
+ }
4511
+ if (!isRecord5(value)) {
4512
+ return null;
4513
+ }
4514
+ const id = value.id;
4515
+ return typeof id === "number" || typeof id === "string" ? id : null;
3863
4516
  };
3864
- var pageNodeTypes = Object.keys(defaultNodeData).map((type) => ({
3865
- type,
3866
- displayName: nodeTypeLabels[type] || type,
3867
- description: `Page node for ${nodeTypeLabels[type] || type}`,
3868
- getDefaultData: () => {
3869
- const migrated = migrateBlockToSettingsV2(structuredClone(defaultNodeData[type]));
3870
- const { blockType: _ignoredBlockType, ...data } = migrated;
3871
- return data;
4517
+ var asLayoutArray = (value) => toRecordArray(value);
4518
+ var hydrateRelationship = (valueFromStudio, valueFromLayout) => {
4519
+ if (isRecord5(valueFromStudio)) {
4520
+ return valueFromStudio;
3872
4521
  }
3873
- }));
3874
- var validatePageDocument = (document) => {
3875
- const issues = [];
3876
- if (!document.title || document.title.trim().length === 0) {
3877
- issues.push({
3878
- code: "pages.title.required",
3879
- message: "Page title is required before publishing.",
3880
- path: "title",
3881
- severity: "error"
3882
- });
4522
+ if (!isRecord5(valueFromLayout)) {
4523
+ return valueFromStudio;
3883
4524
  }
3884
- if (document.nodes.length === 0) {
3885
- issues.push({
3886
- code: "pages.nodes.required",
3887
- message: "At least one section is required.",
3888
- path: "nodes",
3889
- severity: "error"
3890
- });
4525
+ const studioID = getRelationID(valueFromStudio);
4526
+ const layoutID = getRelationID(valueFromLayout);
4527
+ if (studioID === null || layoutID === null || String(studioID) !== String(layoutID)) {
4528
+ return valueFromStudio;
3891
4529
  }
3892
- document.nodes.forEach((node, index) => {
3893
- if (node.type === "hero" && typeof node.data.headline !== "string") {
3894
- issues.push({
3895
- code: "pages.hero.headline",
3896
- message: "Hero section requires a headline.",
3897
- path: `nodes.${index}.data.headline`,
3898
- severity: "error"
3899
- });
3900
- }
3901
- });
3902
- return issues;
4530
+ return valueFromLayout;
3903
4531
  };
3904
- var pagePaletteGroups = [
3905
- {
3906
- id: "page-core",
3907
- label: "Core Sections",
3908
- items: [
3909
- { nodeType: "hero", title: "Hero", description: "Top-of-page headline and CTA" },
3910
- { nodeType: "featureGrid", title: "Feature Grid", description: "Service or value cards" },
3911
- { nodeType: "stats", title: "Stats", description: "Key performance highlights" },
3912
- { nodeType: "logoWall", title: "Logo Wall", description: "Trust logos and badges" },
3913
- { nodeType: "beforeAfter", title: "Before / After", description: "Visual before-and-after gallery" },
3914
- { nodeType: "richText", title: "Rich Text", description: "Long-form content area" },
3915
- { nodeType: "media", title: "Media", description: "Image/video section" },
3916
- { nodeType: "testimonials", title: "Testimonials", description: "Social proof quotes" },
3917
- { nodeType: "faq", title: "FAQ", description: "Question-and-answer section" },
3918
- { nodeType: "cta", title: "Call To Action", description: "Conversion strip with button" },
3919
- { nodeType: "formEmbed", title: "Form Embed", description: "Lead capture form" },
3920
- { nodeType: "bookingEmbed", title: "Booking Embed", description: "Scheduling panel" }
3921
- ]
3922
- }
3923
- ];
3924
- var pageInspectorPanelRegistry = Object.keys(defaultNodeData).map((nodeType) => ({
3925
- nodeType,
3926
- panelID: `${nodeType}-panel`,
3927
- panelLabel: `${nodeTypeLabels[nodeType] || nodeType} Settings`
3928
- }));
3929
- var resolvePanelFields = (nodeType) => inspectorDefinitionByBlockType[nodeType]?.fields.map((field) => ({
3930
- advanced: field.advanced,
3931
- group: field.group,
3932
- inlineEditable: field.inlineEditable,
3933
- key: field.key,
3934
- label: field.label,
3935
- type: field.type
3936
- })) || [];
3937
- var pageInspectorPanels = pageInspectorPanelRegistry.map((entry) => ({
3938
- fields: resolvePanelFields(entry.nodeType),
3939
- id: entry.panelID,
3940
- label: entry.panelLabel,
3941
- nodeType: entry.nodeType
3942
- }));
3943
- var pageStudioModuleManifest = {
3944
- id: "studio-pages",
3945
- version: "0.1.0-beta.0",
3946
- displayName: "Pages Studio Module",
3947
- nodeTypes: pageNodeTypes,
3948
- paletteGroups: pagePaletteGroups,
3949
- inspectorPanels: pageInspectorPanels,
3950
- validators: [validatePageDocument],
3951
- migrations: pageStudioMigrations,
3952
- compiler: {
3953
- compileNode: (node) => {
3954
- const normalized = migrateBlockToSettingsV2({
3955
- blockType: node.type,
3956
- ...node.data
4532
+ var hydrateDocumentWithLayoutRelations = (document, layout) => {
4533
+ const nextNodes = document.nodes.map((node, index) => {
4534
+ const layoutBlock = layout[index];
4535
+ if (!isRecord5(layoutBlock)) {
4536
+ return node;
4537
+ }
4538
+ if (typeof layoutBlock.blockType !== "string" || layoutBlock.blockType !== node.type) {
4539
+ return node;
4540
+ }
4541
+ const nextData = { ...node.data };
4542
+ if (node.type === "hero") {
4543
+ nextData.media = hydrateRelationship(nextData.media, layoutBlock.media);
4544
+ }
4545
+ if (node.type === "media") {
4546
+ nextData.image = hydrateRelationship(nextData.image, layoutBlock.image);
4547
+ }
4548
+ if (node.type === "featureGrid" || node.type === "logoWall" || node.type === "beforeAfter") {
4549
+ const studioItems = Array.isArray(nextData.items) ? nextData.items : [];
4550
+ const layoutItems = Array.isArray(layoutBlock.items) ? layoutBlock.items : [];
4551
+ nextData.items = studioItems.map((rawStudioItem, itemIndex) => {
4552
+ if (!isRecord5(rawStudioItem)) {
4553
+ return rawStudioItem;
4554
+ }
4555
+ const layoutItem = layoutItems[itemIndex];
4556
+ if (!isRecord5(layoutItem)) {
4557
+ return rawStudioItem;
4558
+ }
4559
+ const nextItem = { ...rawStudioItem };
4560
+ nextItem.media = hydrateRelationship(nextItem.media, layoutItem.media);
4561
+ nextItem.beforeMedia = hydrateRelationship(nextItem.beforeMedia, layoutItem.beforeMedia);
4562
+ nextItem.afterMedia = hydrateRelationship(nextItem.afterMedia, layoutItem.afterMedia);
4563
+ return nextItem;
3957
4564
  });
3958
- return {
3959
- id: node.id,
3960
- ...normalized
3961
- };
3962
4565
  }
3963
- },
3964
- permissions: [
3965
- { action: "read", role: "admin" },
3966
- { action: "read", role: "editor" },
3967
- { action: "read", role: "client" },
3968
- { action: "update", role: "admin" },
3969
- { action: "update", role: "editor" },
3970
- { action: "update", role: "client" },
3971
- { action: "publish", role: "admin" },
3972
- { action: "publish", role: "editor" }
3973
- ]
3974
- };
3975
- var ensureNodeID = (inputID, index) => {
3976
- if (typeof inputID === "string" && inputID.length > 0) {
3977
- return inputID;
3978
- }
3979
- return `node-${index + 1}`;
3980
- };
3981
- var layoutToStudioDocument = (layout, title, metadata) => {
3982
- const nodes = layout.filter((block) => typeof block.blockType === "string").map((rawBlock, index) => {
3983
- const block = migrateBlockToSettingsV2(rawBlock);
3984
- const blockType = String(block.blockType);
3985
- const { id, blockType: _ignoredBlockType, ...data } = block;
3986
4566
  return {
3987
- id: ensureNodeID(id, index),
3988
- type: blockType,
3989
- data
4567
+ ...node,
4568
+ data: nextData
3990
4569
  };
3991
4570
  });
3992
4571
  return {
3993
- metadata,
3994
- schemaVersion: 1,
3995
- title,
3996
- nodes,
3997
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4572
+ ...document,
4573
+ nodes: nextNodes
3998
4574
  };
3999
4575
  };
4000
- var studioDocumentToLayout = (document) => document.nodes.map(
4001
- (node) => migrateBlockToSettingsV2({
4002
- id: node.id,
4003
- blockType: node.type,
4004
- ...node.data
4005
- })
4006
- );
4007
- var createDefaultStudioDocument = (title) => ({
4008
- metadata: {},
4576
+ var normalizeDocument = (document) => ({
4577
+ ...document,
4009
4578
  schemaVersion: 1,
4010
- title,
4011
- nodes: [],
4012
4579
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
4013
4580
  });
4014
-
4015
- // src/nextjs/queries/pages.ts
4016
- var PAGE_QUERY_CACHE_VERSION = "v4-published-only-public";
4017
- function withStudioDocumentLayout(page) {
4018
- if (!page) {
4019
- return null;
4020
- }
4021
- try {
4022
- const studioDocument = assertStudioDocumentV1(page.studioDocument);
4023
- const compiledLayout = studioDocumentToLayout(studioDocument);
4024
- if (Array.isArray(compiledLayout) && compiledLayout.length > 0) {
4025
- return {
4026
- ...page,
4027
- layout: compiledLayout
4028
- };
4029
- }
4030
- } catch {
4031
- }
4032
- return page;
4033
- }
4034
- function normalizePath(segments) {
4035
- if (!segments || segments.length === 0) {
4036
- return "/";
4037
- }
4038
- const cleaned = segments.map((segment) => segment.trim()).filter(Boolean).join("/");
4039
- return cleaned.length > 0 ? `/${cleaned}` : "/";
4040
- }
4041
- async function queryPageByPath(payload, path2, draft) {
4042
- const pathWhere = {
4043
- path: {
4044
- equals: path2
4581
+ var normalizeLegacyHeroDefaults = (document) => {
4582
+ const nodes = document.nodes.map((node) => {
4583
+ if (node.type !== "hero" || !isRecord5(node.data)) {
4584
+ return node;
4045
4585
  }
4046
- };
4047
- const publishedWhere = {
4048
- _status: {
4049
- equals: "published"
4586
+ const nextData = { ...node.data };
4587
+ const heroBackgroundColor = typeof nextData.backgroundColor === "string" ? nextData.backgroundColor.trim().toLowerCase() : "";
4588
+ const sectionBackgroundMode = typeof nextData.sectionBackgroundMode === "string" ? nextData.sectionBackgroundMode : "none";
4589
+ const hasBackgroundImageURL = typeof nextData.backgroundImageURL === "string" && nextData.backgroundImageURL.trim().length > 0;
4590
+ const mediaRelation = getRelationID(nextData.media);
4591
+ if (heroBackgroundColor === "#124a37" && sectionBackgroundMode === "none" && !hasBackgroundImageURL && mediaRelation === null) {
4592
+ nextData.backgroundColor = "";
4050
4593
  }
4051
- };
4052
- const result = await payload.find({
4053
- collection: "pages",
4054
- depth: 2,
4055
- draft,
4056
- limit: 1,
4057
- overrideAccess: false,
4058
- where: draft ? pathWhere : { and: [pathWhere, publishedWhere] }
4059
- });
4060
- if (result.docs.length > 0) {
4061
- return withStudioDocumentLayout(result.docs[0] || null);
4062
- }
4063
- if (path2 === "/") {
4064
- const homeWhere = {
4065
- slug: {
4066
- equals: "home"
4067
- }
4594
+ return {
4595
+ ...node,
4596
+ data: nextData
4068
4597
  };
4069
- const homeResult = await payload.find({
4070
- collection: "pages",
4071
- depth: 2,
4072
- draft,
4073
- limit: 1,
4074
- overrideAccess: false,
4075
- where: draft ? homeWhere : { and: [homeWhere, publishedWhere] }
4076
- });
4077
- return withStudioDocumentLayout(homeResult.docs[0] || null);
4598
+ });
4599
+ return {
4600
+ ...document,
4601
+ nodes
4602
+ };
4603
+ };
4604
+ var assertCanPublish = (issues) => {
4605
+ const publishErrors = issues.filter((issue) => issue.severity === "error");
4606
+ if (publishErrors.length > 0) {
4607
+ throw new Error(`Cannot publish page: ${publishErrors[0].message}`);
4078
4608
  }
4079
- return null;
4080
- }
4081
- function createPageQueries(getPayloadClient, contentTag = "website-content") {
4082
- const getPublishedPageByPathCached = (0, import_cache.unstable_cache)(
4083
- async (path2) => {
4084
- const payload = await getPayloadClient();
4085
- return queryPageByPath(payload, path2, false);
4086
- },
4087
- ["page-by-path", PAGE_QUERY_CACHE_VERSION],
4088
- { tags: [contentTag] }
4089
- );
4090
- async function getPageBySegments(segments, draft = false) {
4091
- const path2 = normalizePath(segments);
4092
- const payload = await getPayloadClient();
4093
- if (draft) {
4094
- return queryPageByPath(payload, path2, true);
4609
+ };
4610
+ var getStudioDocumentFromPage = (doc) => {
4611
+ const title = typeof doc.title === "string" ? doc.title : void 0;
4612
+ const layout = asLayoutArray(doc.layout);
4613
+ try {
4614
+ const studioDocument = assertStudioDocumentV1(doc.studioDocument);
4615
+ if (layout.length > 0) {
4616
+ return hydrateDocumentWithLayoutRelations(studioDocument, layout);
4095
4617
  }
4096
- return getPublishedPageByPathCached(path2);
4618
+ return studioDocument;
4619
+ } catch {
4620
+ if (layout.length > 0) {
4621
+ return layoutToStudioDocument(layout, title);
4622
+ }
4623
+ return createEmptyStudioDocument(title);
4097
4624
  }
4098
- async function listPublishedPagePaths() {
4099
- const payload = await getPayloadClient();
4100
- const pages = await payload.find({
4101
- collection: "pages",
4102
- depth: 0,
4103
- draft: false,
4104
- limit: 1e3,
4105
- pagination: false,
4625
+ };
4626
+ var createStudioPageService = ({
4627
+ collectionSlug = "pages",
4628
+ modules,
4629
+ payload,
4630
+ user
4631
+ }) => ({
4632
+ getPageForStudio: async (pageID) => {
4633
+ const page = await payload.findByID({
4634
+ collection: collectionSlug,
4635
+ depth: 2,
4636
+ draft: true,
4637
+ id: pageID,
4106
4638
  overrideAccess: false,
4107
- where: {
4108
- _status: {
4109
- equals: "published"
4110
- }
4111
- }
4639
+ user
4112
4640
  });
4113
- return pages.docs.map((doc) => doc.path).filter((path2) => typeof path2 === "string" && path2.length > 0);
4641
+ const studioDocument = getStudioDocumentFromPage(page);
4642
+ return {
4643
+ id: String(page.id),
4644
+ slug: typeof page.slug === "string" ? page.slug : "",
4645
+ studioDocument,
4646
+ title: typeof page.title === "string" ? page.title : "Untitled Page"
4647
+ };
4648
+ },
4649
+ validateStudioDocument: (document) => validateStudioDocument(document, modules),
4650
+ saveDraft: async (pageID, document, _metadata) => {
4651
+ const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
4652
+ const compileResult = compileStudioDocument(normalizedDocument, modules);
4653
+ await payload.update({
4654
+ collection: collectionSlug,
4655
+ data: {
4656
+ _status: "draft",
4657
+ layout: compileResult.layout,
4658
+ studioDocument: normalizedDocument,
4659
+ studioValidationIssues: compileResult.issues,
4660
+ title: normalizedDocument.title
4661
+ },
4662
+ id: pageID,
4663
+ overrideAccess: false,
4664
+ user
4665
+ });
4666
+ return {
4667
+ id: pageID,
4668
+ status: "draft"
4669
+ };
4670
+ },
4671
+ publish: async (pageID, document, _metadata) => {
4672
+ const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
4673
+ const compileResult = compileStudioDocument(normalizedDocument, modules);
4674
+ assertCanPublish(compileResult.issues);
4675
+ await payload.update({
4676
+ collection: collectionSlug,
4677
+ data: {
4678
+ _status: "published",
4679
+ layout: compileResult.layout,
4680
+ studioDocument: normalizedDocument,
4681
+ studioValidationIssues: compileResult.issues,
4682
+ title: normalizedDocument.title
4683
+ },
4684
+ id: pageID,
4685
+ overrideAccess: false,
4686
+ user
4687
+ });
4688
+ return {
4689
+ id: pageID,
4690
+ status: "published"
4691
+ };
4114
4692
  }
4115
- function pathToSegments(path2) {
4116
- if (!path2 || path2 === "/") {
4117
- return [];
4118
- }
4119
- return path2.split("/").filter(Boolean);
4693
+ });
4694
+ var toEditorInitialDoc = (payloadPage) => ({
4695
+ layout: studioDocumentToLayout(payloadPage.studioDocument),
4696
+ slug: typeof payloadPage.slug === "string" ? payloadPage.slug : "",
4697
+ studioDocument: payloadPage.studioDocument,
4698
+ title: payloadPage.title
4699
+ });
4700
+
4701
+ // src/studio-pages/migrations.ts
4702
+ var isRecord6 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
4703
+ var assertPageStudioDocumentV1 = (value) => {
4704
+ if (!isRecord6(value)) {
4705
+ throw new Error("Studio document must be an object");
4706
+ }
4707
+ if (value.schemaVersion !== 1) {
4708
+ throw new Error("Unsupported studio schemaVersion");
4709
+ }
4710
+ if (!Array.isArray(value.nodes)) {
4711
+ throw new Error("Studio document nodes must be an array");
4120
4712
  }
4713
+ const nodes = value.nodes.map((node, index) => {
4714
+ if (!isRecord6(node)) {
4715
+ throw new Error(`Node at index ${index} must be an object`);
4716
+ }
4717
+ if (typeof node.id !== "string" || node.id.length === 0) {
4718
+ throw new Error(`Node at index ${index} has invalid id`);
4719
+ }
4720
+ if (typeof node.type !== "string" || node.type.length === 0) {
4721
+ throw new Error(`Node at index ${index} has invalid type`);
4722
+ }
4723
+ if (!isRecord6(node.data)) {
4724
+ throw new Error(`Node at index ${index} has invalid data`);
4725
+ }
4726
+ return {
4727
+ data: node.data,
4728
+ id: node.id,
4729
+ type: node.type
4730
+ };
4731
+ });
4121
4732
  return {
4122
- getPageBySegments,
4123
- listPublishedPagePaths,
4124
- pathToSegments
4733
+ metadata: isRecord6(value.metadata) ? value.metadata : void 0,
4734
+ nodes,
4735
+ schemaVersion: 1,
4736
+ title: typeof value.title === "string" ? value.title : void 0,
4737
+ updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : void 0
4125
4738
  };
4126
- }
4739
+ };
4740
+ var migratePageNodeToSettingsV2 = (node) => {
4741
+ const normalized = migrateBlockToSettingsV2({
4742
+ blockType: node.type,
4743
+ ...node.data
4744
+ });
4745
+ const { blockType: _ignoredBlockType, ...data } = normalized;
4746
+ return {
4747
+ ...node,
4748
+ data
4749
+ };
4750
+ };
4751
+ var migratePageDocumentSettingsToV2 = (value) => {
4752
+ const current = assertPageStudioDocumentV1(value);
4753
+ return {
4754
+ ...current,
4755
+ nodes: current.nodes.map(migratePageNodeToSettingsV2)
4756
+ };
4757
+ };
4758
+ var pageStudioMigrations = [
4759
+ {
4760
+ fromVersion: 1,
4761
+ migrate: migratePageDocumentSettingsToV2,
4762
+ toVersion: 1
4763
+ }
4764
+ ];
4127
4765
 
4128
- // src/nextjs/queries/site.ts
4129
- var import_cache2 = require("next/cache");
4130
- function createSiteQueries(getPayloadClient, contentTag = "website-content") {
4131
- const getSiteSettingsCached = (0, import_cache2.unstable_cache)(
4132
- async () => {
4133
- const payload = await getPayloadClient();
4134
- const settings = await payload.findGlobal({
4135
- slug: "site-settings",
4136
- depth: 1
4137
- });
4138
- return settings;
4139
- },
4140
- ["site-settings-global"],
4141
- { tags: [contentTag] }
4142
- );
4143
- const getHeaderCached = (0, import_cache2.unstable_cache)(
4144
- async () => {
4145
- const payload = await getPayloadClient();
4146
- const header = await payload.findGlobal({
4147
- slug: "header",
4148
- depth: 1
4149
- });
4150
- return header;
4151
- },
4152
- ["header-global"],
4153
- { tags: [contentTag] }
4154
- );
4155
- const getFooterCached = (0, import_cache2.unstable_cache)(
4156
- async () => {
4157
- const payload = await getPayloadClient();
4158
- const footer = await payload.findGlobal({
4159
- slug: "footer",
4160
- depth: 1
4161
- });
4162
- return footer;
4163
- },
4164
- ["footer-global"],
4165
- { tags: [contentTag] }
4166
- );
4167
- const getSocialMediaCached = (0, import_cache2.unstable_cache)(
4168
- async () => {
4169
- const payload = await getPayloadClient();
4170
- const socialMedia = await payload.findGlobal({
4171
- slug: "social-media",
4172
- depth: 1
4173
- });
4174
- return socialMedia;
4766
+ // src/studio-pages/index.ts
4767
+ var withSectionStyleDefaults = (value) => ({
4768
+ ...sectionStyleDefaults,
4769
+ ...value
4770
+ });
4771
+ var defaultNodeData = {
4772
+ bookingEmbed: {
4773
+ ...withSectionStyleDefaults({}),
4774
+ buttonHref: "/contact",
4775
+ buttonLabel: "Book Consultation",
4776
+ description: "Let visitors book a consultation.",
4777
+ title: "Book a Time"
4778
+ },
4779
+ beforeAfter: withSectionStyleDefaults({
4780
+ itemsPerRow: 2,
4781
+ items: [
4782
+ {
4783
+ description: "Before and after result summary.",
4784
+ imageCornerStyle: "rounded",
4785
+ imageFit: "cover",
4786
+ imagePosition: "center",
4787
+ label: "Project One"
4788
+ }
4789
+ ],
4790
+ subtitle: "Show visual proof from real projects.",
4791
+ title: "Before & After Results"
4792
+ }),
4793
+ cta: {
4794
+ ...withSectionStyleDefaults({}),
4795
+ backgroundColor: "",
4796
+ bullets: [],
4797
+ buttonHref: "/contact",
4798
+ buttonLabel: "Contact Us",
4799
+ description: "Optional supporting copy.",
4800
+ eyebrow: "",
4801
+ headline: "Ready to get started?",
4802
+ imageURL: "",
4803
+ style: "light"
4804
+ },
4805
+ faq: {
4806
+ ...withSectionStyleDefaults({}),
4807
+ items: [{ answer: "Answer goes here.", question: "Frequently asked question?" }],
4808
+ title: "Frequently Asked Questions"
4809
+ },
4810
+ featureGrid: {
4811
+ ...withSectionStyleDefaults({}),
4812
+ itemsPerRow: 3,
4813
+ items: [
4814
+ { description: "Explain this point.", iconType: "badge", icon: "01", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature One" },
4815
+ { description: "Explain this point.", iconType: "badge", icon: "02", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature Two" },
4816
+ { description: "Explain this point.", iconType: "badge", icon: "03", imageCornerStyle: "rounded", imageFit: "cover", imagePosition: "center", title: "Feature Three" }
4817
+ ],
4818
+ subtitle: "",
4819
+ title: "Section Title",
4820
+ variant: "cards"
4821
+ },
4822
+ formEmbed: {
4823
+ ...withSectionStyleDefaults({}),
4824
+ description: "Collect lead details from visitors.",
4825
+ formType: "quote",
4826
+ submitLabel: "Submit",
4827
+ title: "Request a Quote"
4828
+ },
4829
+ hero: {
4830
+ ...withSectionStyleDefaults({}),
4831
+ backgroundColor: "",
4832
+ backgroundImageURL: "",
4833
+ backgroundOverlayMode: "none",
4834
+ backgroundOverlayOpacity: 45,
4835
+ backgroundOverlayColor: "#000000",
4836
+ backgroundOverlayGradientFrom: "#334b63",
4837
+ backgroundOverlayGradientTo: "#496582",
4838
+ backgroundOverlayGradientAngle: "135",
4839
+ backgroundOverlayGradientFromStrength: 100,
4840
+ backgroundOverlayGradientToStrength: 100,
4841
+ backgroundOverlayGradientStart: 0,
4842
+ backgroundOverlayGradientEnd: 100,
4843
+ backgroundOverlayGradientFeather: 100,
4844
+ backgroundImageCornerStyle: "rounded",
4845
+ backgroundImageFit: "cover",
4846
+ backgroundImagePosition: "center",
4847
+ heroHeight: "sm",
4848
+ headline: "New Hero Section",
4849
+ kicker: "Optional kicker",
4850
+ primaryHref: "/contact",
4851
+ primaryLabel: "Primary Action",
4852
+ secondaryHref: "/services",
4853
+ secondaryLabel: "Secondary Action",
4854
+ subheadline: "Describe your offer clearly for website visitors.",
4855
+ variant: "default"
4856
+ },
4857
+ media: {
4858
+ ...withSectionStyleDefaults({}),
4859
+ caption: "Add a caption",
4860
+ imageCornerStyle: "rounded",
4861
+ imageFit: "cover",
4862
+ imagePosition: "center",
4863
+ size: "default"
4864
+ },
4865
+ logoWall: withSectionStyleDefaults({
4866
+ items: [
4867
+ { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 1" },
4868
+ { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 2" },
4869
+ { imageCornerStyle: "rounded", imageFit: "contain", imagePosition: "center", name: "Trusted Partner 3" }
4870
+ ],
4871
+ subtitle: "Show logos from trusted associations, partners, or collaborators.",
4872
+ title: "Trusted by Great Partners"
4873
+ }),
4874
+ richText: {
4875
+ ...withSectionStyleDefaults({}),
4876
+ cards: [],
4877
+ content: {
4878
+ root: {
4879
+ children: [
4880
+ {
4881
+ children: [
4882
+ {
4883
+ detail: 0,
4884
+ format: 0,
4885
+ mode: "normal",
4886
+ style: "",
4887
+ text: "Write your content here.",
4888
+ type: "text",
4889
+ version: 1
4890
+ }
4891
+ ],
4892
+ direction: "ltr",
4893
+ format: "",
4894
+ indent: 0,
4895
+ type: "paragraph",
4896
+ version: 1
4897
+ }
4898
+ ],
4899
+ direction: "ltr",
4900
+ format: "",
4901
+ indent: 0,
4902
+ type: "root",
4903
+ version: 1
4904
+ }
4175
4905
  },
4176
- ["social-media-global"],
4177
- { tags: [contentTag] }
4178
- );
4179
- async function getSiteSettings(draft = false) {
4180
- if (draft) {
4181
- const payload = await getPayloadClient();
4182
- const settings = await payload.findGlobal({
4183
- slug: "site-settings",
4184
- depth: 1,
4185
- draft: true
4186
- });
4187
- return settings;
4188
- }
4189
- return getSiteSettingsCached();
4906
+ statsItems: [],
4907
+ title: "Section Heading",
4908
+ width: "normal"
4909
+ },
4910
+ testimonials: {
4911
+ ...withSectionStyleDefaults({}),
4912
+ autoRotate: true,
4913
+ items: [{ location: "City, ST", name: "Customer Name", quote: "Customer feedback goes here.", rating: 5 }],
4914
+ rotateIntervalSeconds: 7,
4915
+ title: "What Customers Say",
4916
+ visibleCount: 3
4917
+ },
4918
+ stats: withSectionStyleDefaults({
4919
+ items: [
4920
+ { description: "Average first response", label: "Response Time", value: "24h" },
4921
+ { description: "Client satisfaction score", label: "Satisfaction", value: "4.9/5" },
4922
+ { description: "Recent projects or clients served", label: "Recent Work", value: "150+" }
4923
+ ],
4924
+ subtitle: "Highlight measurable outcomes to build trust quickly.",
4925
+ title: "Performance Highlights"
4926
+ })
4927
+ };
4928
+ var nodeTypeLabels = {
4929
+ bookingEmbed: "Booking Embed",
4930
+ beforeAfter: "Before / After",
4931
+ cta: "Call To Action",
4932
+ faq: "FAQ",
4933
+ featureGrid: "Feature Grid",
4934
+ formEmbed: "Form Embed",
4935
+ hero: "Hero",
4936
+ logoWall: "Logo Wall",
4937
+ media: "Media",
4938
+ richText: "Rich Text",
4939
+ stats: "Stats",
4940
+ testimonials: "Testimonials"
4941
+ };
4942
+ var pageNodeTypes = Object.keys(defaultNodeData).map((type) => ({
4943
+ type,
4944
+ displayName: nodeTypeLabels[type] || type,
4945
+ description: `Page node for ${nodeTypeLabels[type] || type}`,
4946
+ getDefaultData: () => {
4947
+ const migrated = migrateBlockToSettingsV2(structuredClone(defaultNodeData[type]));
4948
+ const { blockType: _ignoredBlockType, ...data } = migrated;
4949
+ return data;
4190
4950
  }
4191
- async function getHeader(draft = false) {
4192
- if (draft) {
4193
- const payload = await getPayloadClient();
4194
- const header = await payload.findGlobal({
4195
- slug: "header",
4196
- depth: 1,
4197
- draft: true
4198
- });
4199
- return header;
4200
- }
4201
- return getHeaderCached();
4951
+ }));
4952
+ var validatePageDocument = (document) => {
4953
+ const issues = [];
4954
+ if (!document.title || document.title.trim().length === 0) {
4955
+ issues.push({
4956
+ code: "pages.title.required",
4957
+ message: "Page title is required before publishing.",
4958
+ path: "title",
4959
+ severity: "error"
4960
+ });
4202
4961
  }
4203
- async function getFooter(draft = false) {
4204
- if (draft) {
4205
- const payload = await getPayloadClient();
4206
- const footer = await payload.findGlobal({
4207
- slug: "footer",
4208
- depth: 1,
4209
- draft: true
4210
- });
4211
- return footer;
4212
- }
4213
- return getFooterCached();
4962
+ if (document.nodes.length === 0) {
4963
+ issues.push({
4964
+ code: "pages.nodes.required",
4965
+ message: "At least one section is required.",
4966
+ path: "nodes",
4967
+ severity: "error"
4968
+ });
4214
4969
  }
4215
- async function getSocialMedia(draft = false) {
4216
- if (draft) {
4217
- const payload = await getPayloadClient();
4218
- const socialMedia = await payload.findGlobal({
4219
- slug: "social-media",
4220
- depth: 1,
4221
- draft: true
4970
+ document.nodes.forEach((node, index) => {
4971
+ if (node.type === "hero" && typeof node.data.headline !== "string") {
4972
+ issues.push({
4973
+ code: "pages.hero.headline",
4974
+ message: "Hero section requires a headline.",
4975
+ path: `nodes.${index}.data.headline`,
4976
+ severity: "error"
4222
4977
  });
4223
- return socialMedia;
4224
4978
  }
4225
- return getSocialMediaCached();
4226
- }
4227
- return {
4228
- getSiteSettings,
4229
- getHeader,
4230
- getFooter,
4231
- getSocialMedia
4232
- };
4233
- }
4234
-
4235
- // src/nextjs/utilities/media.ts
4236
- function resolveMedia(media) {
4237
- if (!media) {
4238
- return null;
4239
- }
4240
- if (typeof media === "number" || typeof media === "string") {
4241
- return null;
4242
- }
4243
- if (media.url) {
4244
- return {
4245
- url: media.url,
4246
- alt: media.alt || ""
4247
- };
4248
- }
4249
- if (media.filename) {
4250
- return {
4251
- url: `/media/${media.filename}`,
4252
- alt: media.alt || ""
4253
- };
4254
- }
4255
- return null;
4256
- }
4257
-
4258
- // src/nextjs/utilities/socialMedia.ts
4259
- function resolveSocialMediaLinks(data) {
4260
- const profiles = data?.profiles;
4261
- if (!profiles || typeof profiles !== "object") {
4262
- return [];
4979
+ });
4980
+ return issues;
4981
+ };
4982
+ var pagePaletteGroups = [
4983
+ {
4984
+ id: "page-core",
4985
+ label: "Core Sections",
4986
+ items: [
4987
+ { nodeType: "hero", title: "Hero", description: "Top-of-page headline and CTA" },
4988
+ { nodeType: "featureGrid", title: "Feature Grid", description: "Service or value cards" },
4989
+ { nodeType: "stats", title: "Stats", description: "Key performance highlights" },
4990
+ { nodeType: "logoWall", title: "Logo Wall", description: "Trust logos and badges" },
4991
+ { nodeType: "beforeAfter", title: "Before / After", description: "Visual before-and-after gallery" },
4992
+ { nodeType: "richText", title: "Rich Text", description: "Long-form content area" },
4993
+ { nodeType: "media", title: "Media", description: "Image/video section" },
4994
+ { nodeType: "testimonials", title: "Testimonials", description: "Social proof quotes" },
4995
+ { nodeType: "faq", title: "FAQ", description: "Question-and-answer section" },
4996
+ { nodeType: "cta", title: "Call To Action", description: "Conversion strip with button" },
4997
+ { nodeType: "formEmbed", title: "Form Embed", description: "Lead capture form" },
4998
+ { nodeType: "bookingEmbed", title: "Booking Embed", description: "Scheduling panel" }
4999
+ ]
4263
5000
  }
4264
- return SOCIAL_MEDIA_PLATFORMS.reduce((acc, platform) => {
4265
- const profile = profiles[platform];
4266
- if (!profile || typeof profile !== "object") {
4267
- return acc;
4268
- }
4269
- const url = typeof profile.url === "string" ? profile.url.trim() : "";
4270
- if (!url) {
4271
- return acc;
5001
+ ];
5002
+ var pageInspectorPanelRegistry = Object.keys(defaultNodeData).map((nodeType) => ({
5003
+ nodeType,
5004
+ panelID: `${nodeType}-panel`,
5005
+ panelLabel: `${nodeTypeLabels[nodeType] || nodeType} Settings`
5006
+ }));
5007
+ var resolvePanelFields = (nodeType) => inspectorDefinitionByBlockType[nodeType]?.fields.map((field) => ({
5008
+ advanced: field.advanced,
5009
+ group: field.group,
5010
+ inlineEditable: field.inlineEditable,
5011
+ key: field.key,
5012
+ label: field.label,
5013
+ type: field.type
5014
+ })) || [];
5015
+ var pageInspectorPanels = pageInspectorPanelRegistry.map((entry) => ({
5016
+ fields: resolvePanelFields(entry.nodeType),
5017
+ id: entry.panelID,
5018
+ label: entry.panelLabel,
5019
+ nodeType: entry.nodeType
5020
+ }));
5021
+ var pageStudioModuleManifest = {
5022
+ id: "studio-pages",
5023
+ version: "0.1.0-beta.0",
5024
+ displayName: "Pages Studio Module",
5025
+ nodeTypes: pageNodeTypes,
5026
+ paletteGroups: pagePaletteGroups,
5027
+ inspectorPanels: pageInspectorPanels,
5028
+ validators: [validatePageDocument],
5029
+ migrations: pageStudioMigrations,
5030
+ compiler: {
5031
+ compileNode: (node) => {
5032
+ const normalized = migrateBlockToSettingsV2({
5033
+ blockType: node.type,
5034
+ ...node.data
5035
+ });
5036
+ return {
5037
+ id: node.id,
5038
+ ...normalized
5039
+ };
4272
5040
  }
4273
- const icon = typeof profile.icon === "string" && profile.icon.trim().length > 0 ? profile.icon.trim() : SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM[platform];
4274
- acc.push({
4275
- icon,
4276
- label: SOCIAL_MEDIA_PLATFORM_LABELS[platform],
4277
- platform,
4278
- url
4279
- });
4280
- return acc;
4281
- }, []);
4282
- }
5041
+ },
5042
+ permissions: [
5043
+ { action: "read", role: "admin" },
5044
+ { action: "read", role: "editor" },
5045
+ { action: "read", role: "client" },
5046
+ { action: "update", role: "admin" },
5047
+ { action: "update", role: "editor" },
5048
+ { action: "update", role: "client" },
5049
+ { action: "publish", role: "admin" },
5050
+ { action: "publish", role: "editor" }
5051
+ ]
5052
+ };
4283
5053
  // Annotate the CommonJS export names for ESM import in node:
4284
5054
  0 && (module.exports = {
4285
5055
  admin,