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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +6 -1
  2. package/bin/init.js +329 -0
  3. package/dist/admin/index.d.mts +1 -1
  4. package/dist/admin/index.d.ts +1 -1
  5. package/dist/admin/index.js +61 -20
  6. package/dist/admin/index.mjs +1 -1
  7. package/dist/blocks/index.mjs +1 -2
  8. package/dist/{chunk-NGLIA2OE.mjs → chunk-DTHBPJXU.mjs} +193 -23
  9. package/dist/{chunk-JQAHXYAM.mjs → chunk-KMYMSR7W.mjs} +163 -3
  10. package/dist/{chunk-KHK6RTGC.mjs → chunk-MMJNHBOF.mjs} +61 -20
  11. package/dist/{chunk-ZADL33R6.mjs → chunk-OF6BATTO.mjs} +56 -111
  12. package/dist/chunk-VA545CHJ.mjs +202 -0
  13. package/dist/forms/index.d.mts +25 -0
  14. package/dist/forms/index.d.ts +25 -0
  15. package/dist/forms/index.js +237 -0
  16. package/dist/forms/index.mjs +25 -0
  17. package/dist/forms/server.d.mts +35 -0
  18. package/dist/forms/server.d.ts +35 -0
  19. package/dist/forms/server.js +442 -0
  20. package/dist/forms/server.mjs +228 -0
  21. package/dist/{index-Dv-Alx4h.d.ts → index-0xXWuOuz.d.ts} +35 -5
  22. package/dist/{index-D5zrOdyv.d.mts → index-BU82akSL.d.mts} +35 -5
  23. package/dist/{index-DLfPOqYA.d.mts → index-BbTcimgH.d.mts} +1 -0
  24. package/dist/{index-D8BNfUJb.d.mts → index-Cen-9wcc.d.mts} +17 -5
  25. package/dist/{index-BV0vEGl6.d.ts → index-DV-nW43e.d.ts} +1 -0
  26. package/dist/{index-DD_E2UfJ.d.ts → index-gxcMPpm-.d.ts} +17 -5
  27. package/dist/index.d.mts +3 -3
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +302 -151
  30. package/dist/index.mjs +11 -12
  31. package/dist/nextjs/index.d.mts +1 -1
  32. package/dist/nextjs/index.d.ts +1 -1
  33. package/dist/nextjs/index.js +56 -111
  34. package/dist/nextjs/index.mjs +1 -1
  35. package/dist/studio-pages/index.d.mts +1 -1
  36. package/dist/studio-pages/index.d.ts +1 -1
  37. package/dist/studio-pages/index.js +1802 -20
  38. package/dist/studio-pages/index.mjs +10 -2
  39. package/package.json +18 -2
  40. package/dist/chunk-OQSEJXC4.mjs +0 -166
package/README.md CHANGED
@@ -22,8 +22,10 @@ Prefer explicit subpath imports.
22
22
  import { configureAdmin, type StudioSection } from '@orion-studios/payload-studio/admin'
23
23
  import { createPayloadClient, createPageQueries, createSiteQueries } from '@orion-studios/payload-studio/nextjs'
24
24
  import { defaultPageLayoutBlocks } from '@orion-studios/payload-studio/blocks'
25
- import { pageStudioModuleManifest } from '@orion-studios/payload-studio/studio-pages'
25
+ import { createStudioPagesCollection, pageStudioModuleManifest } from '@orion-studios/payload-studio/studio-pages'
26
26
  import { withImageUploadOptimization } from '@orion-studios/payload-studio/studio'
27
+ import { validateEmail, formatPhoneUS } from '@orion-studios/payload-studio/forms'
28
+ import { createFormSubmissionHandler } from '@orion-studios/payload-studio/forms/server'
27
29
  ```
28
30
 
29
31
  The root `@orion-studios/payload-studio` namespace export remains available for backward compatibility, but new integrations should treat subpath imports as the public contract.
@@ -32,6 +34,7 @@ The root `@orion-studios/payload-studio` namespace export remains available for
32
34
 
33
35
  ```bash
34
36
  npm install @orion-studios/payload-studio
37
+ npx payload-studio-init # scaffolds app scripts, lib wrappers, and the form submission route
35
38
  ```
36
39
 
37
40
  ```ts
@@ -70,6 +73,8 @@ const adminConfig = configureAdmin({
70
73
  - `@orion-studios/payload-studio/admin-app/client`
71
74
  - `@orion-studios/payload-studio/admin-app/styles.css`
72
75
  - `@orion-studios/payload-studio/blocks`
76
+ - `@orion-studios/payload-studio/forms`
77
+ - `@orion-studios/payload-studio/forms/server`
73
78
  - `@orion-studios/payload-studio/nextjs`
74
79
  - `@orion-studios/payload-studio/studio`
75
80
  - `@orion-studios/payload-studio/studio-pages`
package/bin/init.js ADDED
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Scaffolds the app-side files required by the Studio Website Setup Guide.
5
+ *
6
+ * Usage (from the consuming app root):
7
+ * npx payload-studio-init
8
+ *
9
+ * Existing files are never overwritten — safe to re-run after upgrades.
10
+ */
11
+
12
+ const fs = require('fs')
13
+ const path = require('path')
14
+
15
+ const cwd = process.cwd()
16
+
17
+ const hasSrcDir = fs.existsSync(path.join(cwd, 'src'))
18
+ const srcRoot = hasSrcDir ? 'src' : '.'
19
+
20
+ const FILES = [
21
+ {
22
+ file: 'scripts/clean-next-cache.cjs',
23
+ contents: `#!/usr/bin/env node
24
+
25
+ // Clears stale Next.js build caches before dev/build runs.
26
+ // Pass --all to remove the entire .next directory instead of just the cache.
27
+
28
+ const fs = require('fs')
29
+ const path = require('path')
30
+
31
+ const all = process.argv.includes('--all')
32
+ const target = path.join(process.cwd(), '.next', all ? '' : 'cache')
33
+
34
+ try {
35
+ fs.rmSync(target, { recursive: true, force: true })
36
+ console.log('[clean-next-cache] removed ' + path.relative(process.cwd(), target || '.next'))
37
+ } catch (error) {
38
+ console.warn('[clean-next-cache] skipped: ' + error.message)
39
+ }
40
+ `,
41
+ },
42
+ {
43
+ file: 'scripts/schema-sync.ts',
44
+ contents: `/**
45
+ * Pushes the current Payload schema to the database.
46
+ *
47
+ * Run via: npm run schema:sync
48
+ * The PAYLOAD_SCHEMA_PUSH=true env flag is set by the npm script — schema push
49
+ * stays opt-in only, per the Studio contract.
50
+ */
51
+
52
+ import config from '../${srcRoot}/payload.config'
53
+ import { getPayload } from 'payload'
54
+
55
+ async function run() {
56
+ if (process.env.PAYLOAD_SCHEMA_PUSH !== 'true') {
57
+ console.error('Refusing to run: set PAYLOAD_SCHEMA_PUSH=true (use \`npm run schema:sync\`).')
58
+ process.exit(1)
59
+ }
60
+
61
+ const payload = await getPayload({ config })
62
+ console.log('[schema-sync] schema synchronized')
63
+ if (typeof payload.db?.destroy === 'function') {
64
+ await payload.db.destroy()
65
+ }
66
+ process.exit(0)
67
+ }
68
+
69
+ run().catch((error) => {
70
+ console.error('[schema-sync] failed:', error)
71
+ process.exit(1)
72
+ })
73
+ `,
74
+ },
75
+ {
76
+ file: 'scripts/seed.ts',
77
+ contents: `/**
78
+ * Seeds the CMS with the canonical default pages from src/lib/site-page-layouts.
79
+ * Existing pages are left untouched — safe to re-run.
80
+ *
81
+ * Run via: npm run seed
82
+ */
83
+
84
+ import config from '../${srcRoot}/payload.config'
85
+ import { getPayload } from 'payload'
86
+ import { sitePageLayouts } from '../${srcRoot}/lib/site-page-layouts'
87
+
88
+ async function run() {
89
+ const payload = await getPayload({ config })
90
+
91
+ for (const page of sitePageLayouts) {
92
+ const existing = await payload.find({
93
+ collection: 'pages',
94
+ limit: 1,
95
+ where: { slug: { equals: page.slug } },
96
+ })
97
+
98
+ if (existing.docs.length > 0) {
99
+ console.log('[seed] skip existing page: ' + page.slug)
100
+ continue
101
+ }
102
+
103
+ await payload.create({
104
+ collection: 'pages',
105
+ data: {
106
+ _status: 'published',
107
+ layout: page.layout,
108
+ slug: page.slug,
109
+ title: page.title,
110
+ },
111
+ })
112
+ console.log('[seed] created page: ' + page.slug)
113
+ }
114
+
115
+ if (typeof payload.db?.destroy === 'function') {
116
+ await payload.db.destroy()
117
+ }
118
+ process.exit(0)
119
+ }
120
+
121
+ run().catch((error) => {
122
+ console.error('[seed] failed:', error)
123
+ process.exit(1)
124
+ })
125
+ `,
126
+ },
127
+ {
128
+ file: 'scripts/sync-page-layouts.ts',
129
+ contents: `/**
130
+ * Syncs the canonical default layouts from src/lib/site-page-layouts into the
131
+ * CMS for pages that have NOT been edited in the Studio builder yet
132
+ * (no studioDocument). Builder-authored pages are never overwritten.
133
+ *
134
+ * Run via: npm run pages:sync-layouts
135
+ */
136
+
137
+ import config from '../${srcRoot}/payload.config'
138
+ import { getPayload } from 'payload'
139
+ import { sitePageLayouts } from '../${srcRoot}/lib/site-page-layouts'
140
+
141
+ async function run() {
142
+ const payload = await getPayload({ config })
143
+
144
+ for (const page of sitePageLayouts) {
145
+ const existing = await payload.find({
146
+ collection: 'pages',
147
+ draft: true,
148
+ limit: 1,
149
+ where: { slug: { equals: page.slug } },
150
+ })
151
+
152
+ const doc = existing.docs[0] as { id: number | string; studioDocument?: unknown } | undefined
153
+
154
+ if (!doc) {
155
+ await payload.create({
156
+ collection: 'pages',
157
+ data: {
158
+ _status: 'published',
159
+ layout: page.layout,
160
+ slug: page.slug,
161
+ title: page.title,
162
+ },
163
+ })
164
+ console.log('[sync-layouts] created page: ' + page.slug)
165
+ continue
166
+ }
167
+
168
+ if (doc.studioDocument) {
169
+ console.log('[sync-layouts] skip builder-owned page: ' + page.slug)
170
+ continue
171
+ }
172
+
173
+ await payload.update({
174
+ collection: 'pages',
175
+ id: doc.id,
176
+ data: { layout: page.layout },
177
+ })
178
+ console.log('[sync-layouts] updated layout for page: ' + page.slug)
179
+ }
180
+
181
+ if (typeof payload.db?.destroy === 'function') {
182
+ await payload.db.destroy()
183
+ }
184
+ process.exit(0)
185
+ }
186
+
187
+ run().catch((error) => {
188
+ console.error('[sync-layouts] failed:', error)
189
+ process.exit(1)
190
+ })
191
+ `,
192
+ },
193
+ {
194
+ file: `${srcRoot}/lib/payload.ts`,
195
+ contents: `import config from '@/payload.config'
196
+ import { createPayloadClient, WEBSITE_CONTENT_TAG } from '@orion-studios/payload-studio/nextjs'
197
+
198
+ export const getPayloadClient = createPayloadClient(config)
199
+ export { WEBSITE_CONTENT_TAG }
200
+ `,
201
+ },
202
+ {
203
+ file: `${srcRoot}/lib/pages.ts`,
204
+ contents: `import { createPageQueries } from '@orion-studios/payload-studio/nextjs'
205
+ import { getPayloadClient, WEBSITE_CONTENT_TAG } from '@/lib/payload'
206
+
207
+ export const { getPageBySegments, listPublishedPagePaths, pathToSegments } = createPageQueries(
208
+ getPayloadClient,
209
+ { contentTag: WEBSITE_CONTENT_TAG },
210
+ )
211
+ `,
212
+ },
213
+ {
214
+ file: `${srcRoot}/lib/site.ts`,
215
+ contents: `import { createSiteQueries } from '@orion-studios/payload-studio/nextjs'
216
+ import { getPayloadClient, WEBSITE_CONTENT_TAG } from '@/lib/payload'
217
+
218
+ export const { getSiteSettings, getHeader, getFooter, getSocialMedia } = createSiteQueries(
219
+ getPayloadClient,
220
+ { contentTag: WEBSITE_CONTENT_TAG },
221
+ )
222
+ `,
223
+ },
224
+ {
225
+ file: `${srcRoot}/lib/site-page-layouts.ts`,
226
+ contents: `/**
227
+ * Canonical default page layouts.
228
+ *
229
+ * This module is the single layout source used by runtime fallback, the seed
230
+ * script, and the sync script, per the Studio Website Setup Guide.
231
+ */
232
+
233
+ export type SitePageLayout = {
234
+ slug: string
235
+ title: string
236
+ layout: Array<Record<string, unknown>>
237
+ }
238
+
239
+ export const sitePageLayouts: SitePageLayout[] = [
240
+ {
241
+ slug: 'home',
242
+ title: 'Home',
243
+ layout: [],
244
+ },
245
+ ]
246
+ `,
247
+ },
248
+ {
249
+ file: `${srcRoot}/app/api/form-submissions/[slug]/route.ts`,
250
+ contents: `import { createFormSubmissionHandler } from '@orion-studios/payload-studio/forms/server'
251
+ import { getPayloadClient } from '@/lib/payload'
252
+
253
+ /**
254
+ * Public form submission boundary. Anonymous users must never write directly
255
+ * to the submissions collection — this route is the only entry point.
256
+ * Origin check, honeypot, minimum fill time, rate limiting, and server-side
257
+ * re-validation are handled by the shared package handler.
258
+ */
259
+ export const POST = createFormSubmissionHandler({
260
+ getPayloadClient,
261
+ })
262
+ `,
263
+ },
264
+ ]
265
+
266
+ const REQUIRED_SCRIPTS = {
267
+ dev: 'node scripts/clean-next-cache.cjs && npm run generate:importmap && next dev --webpack',
268
+ build:
269
+ 'node scripts/clean-next-cache.cjs --all && npm run generate:importmap && next build --webpack',
270
+ lint: 'eslint .',
271
+ typecheck: 'tsc --noEmit',
272
+ verify: 'npm run payload:types && npm run lint && npm run typecheck',
273
+ payload: "NODE_OPTIONS='--import tsx' payload",
274
+ 'payload:types': "NODE_OPTIONS='--import tsx' payload generate:types",
275
+ 'generate:importmap': "NODE_OPTIONS='--import tsx' payload generate:importmap",
276
+ 'schema:sync': 'PAYLOAD_SCHEMA_PUSH=true tsx scripts/schema-sync.ts',
277
+ seed: 'tsx scripts/seed.ts',
278
+ 'pages:sync-layouts': 'tsx scripts/sync-page-layouts.ts',
279
+ }
280
+
281
+ try {
282
+ console.log('Payload Studio app scaffolding\n')
283
+
284
+ let created = 0
285
+ let skipped = 0
286
+
287
+ for (const entry of FILES) {
288
+ const target = path.join(cwd, entry.file)
289
+
290
+ if (fs.existsSync(target)) {
291
+ console.log(' skip (exists) ' + entry.file)
292
+ skipped += 1
293
+ continue
294
+ }
295
+
296
+ fs.mkdirSync(path.dirname(target), { recursive: true })
297
+ fs.writeFileSync(target, entry.contents)
298
+ console.log(' created ' + entry.file)
299
+ created += 1
300
+ }
301
+
302
+ console.log('\n' + created + ' file(s) created, ' + skipped + ' skipped (already present).')
303
+
304
+ // Report which required package.json scripts are missing, without mutating.
305
+ const pkgPath = path.join(cwd, 'package.json')
306
+ if (fs.existsSync(pkgPath)) {
307
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
308
+ const scripts = pkg.scripts || {}
309
+ const missing = Object.entries(REQUIRED_SCRIPTS).filter(([name]) => !scripts[name])
310
+
311
+ if (missing.length > 0) {
312
+ console.log('\nAdd the following to package.json "scripts":\n')
313
+ for (const [name, command] of missing) {
314
+ console.log(' "' + name + '": "' + command.replace(/"/g, '\\"') + '",')
315
+ }
316
+ } else {
317
+ console.log('\nAll required package.json scripts are present.')
318
+ }
319
+ }
320
+
321
+ console.log('\nNext steps:')
322
+ console.log(' 1. Wire payload.config.ts with configureAdmin(...) and createStudioPagesCollection(...)')
323
+ console.log(' 2. npm run payload:types')
324
+ console.log(' 3. npm run generate:importmap')
325
+ console.log(' 4. See docs/studio-website-setup-guide.md for the full contract.')
326
+ } catch (error) {
327
+ console.error('Scaffolding failed:', error)
328
+ process.exit(1)
329
+ }
@@ -1,4 +1,4 @@
1
- export { A as AdminConfig, a as AdminStudioConfig, b as AdminStudioFooterPreviewConfig, c as AdminStudioHeaderPreviewConfig, d as AdminStudioSitePreviewConfig, C as CreateSocialMediaConnectionsFieldOptions, e as CreateSocialMediaGlobalOptions, R as ResolvedStudioDashboardPanel, f as ResolvedStudioSection, S as StudioDashboardPanel, g as StudioDashboardPanelSpan, h as StudioGlobalLink, j as StudioSection, k as StudioSectionCard, l as StudioSectionComponent, m as StudioSectionRole, n as StudioSectionView, o as configureAdmin, p as createHeaderNavItemsField, q as createSocialMediaConnectionsField, r as createSocialMediaGlobal, s as createThemePreferenceField, t as socialMediaConnectionsField, u as themePreferenceField, w as withTooltips } from '../index-DLfPOqYA.mjs';
1
+ export { A as AdminConfig, a as AdminStudioConfig, b as AdminStudioFooterPreviewConfig, c as AdminStudioHeaderPreviewConfig, d as AdminStudioSitePreviewConfig, C as CreateSocialMediaConnectionsFieldOptions, e as CreateSocialMediaGlobalOptions, R as ResolvedStudioDashboardPanel, f as ResolvedStudioSection, S as StudioDashboardPanel, g as StudioDashboardPanelSpan, h as StudioGlobalLink, j as StudioSection, k as StudioSectionCard, l as StudioSectionComponent, m as StudioSectionRole, n as StudioSectionView, o as configureAdmin, p as createHeaderNavItemsField, q as createSocialMediaConnectionsField, r as createSocialMediaGlobal, s as createThemePreferenceField, t as socialMediaConnectionsField, u as themePreferenceField, w as withTooltips } from '../index-BbTcimgH.mjs';
2
2
  export { b as SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM, c as SOCIAL_MEDIA_ICON_OPTIONS, d as SOCIAL_MEDIA_PLATFORMS, e as SOCIAL_MEDIA_PLATFORM_LABELS, S as SocialMediaGlobalData, f as SocialMediaIconLibrary, g as SocialMediaIconOption, a as SocialMediaPlatform, h as SocialMediaProfileData, i as SocialMediaProfilesData } from '../socialMedia-C05Iy-SV.mjs';
3
3
  import 'payload';
4
4
  import '../sitePreviewTypes-BrJwGzJj.mjs';
@@ -1,4 +1,4 @@
1
- export { A as AdminConfig, a as AdminStudioConfig, b as AdminStudioFooterPreviewConfig, c as AdminStudioHeaderPreviewConfig, d as AdminStudioSitePreviewConfig, C as CreateSocialMediaConnectionsFieldOptions, e as CreateSocialMediaGlobalOptions, R as ResolvedStudioDashboardPanel, f as ResolvedStudioSection, S as StudioDashboardPanel, g as StudioDashboardPanelSpan, h as StudioGlobalLink, j as StudioSection, k as StudioSectionCard, l as StudioSectionComponent, m as StudioSectionRole, n as StudioSectionView, o as configureAdmin, p as createHeaderNavItemsField, q as createSocialMediaConnectionsField, r as createSocialMediaGlobal, s as createThemePreferenceField, t as socialMediaConnectionsField, u as themePreferenceField, w as withTooltips } from '../index-BV0vEGl6.js';
1
+ export { A as AdminConfig, a as AdminStudioConfig, b as AdminStudioFooterPreviewConfig, c as AdminStudioHeaderPreviewConfig, d as AdminStudioSitePreviewConfig, C as CreateSocialMediaConnectionsFieldOptions, e as CreateSocialMediaGlobalOptions, R as ResolvedStudioDashboardPanel, f as ResolvedStudioSection, S as StudioDashboardPanel, g as StudioDashboardPanelSpan, h as StudioGlobalLink, j as StudioSection, k as StudioSectionCard, l as StudioSectionComponent, m as StudioSectionRole, n as StudioSectionView, o as configureAdmin, p as createHeaderNavItemsField, q as createSocialMediaConnectionsField, r as createSocialMediaGlobal, s as createThemePreferenceField, t as socialMediaConnectionsField, u as themePreferenceField, w as withTooltips } from '../index-DV-nW43e.js';
2
2
  export { b as SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM, c as SOCIAL_MEDIA_ICON_OPTIONS, d as SOCIAL_MEDIA_PLATFORMS, e as SOCIAL_MEDIA_PLATFORM_LABELS, S as SocialMediaGlobalData, f as SocialMediaIconLibrary, g as SocialMediaIconOption, a as SocialMediaPlatform, h as SocialMediaProfileData, i as SocialMediaProfilesData } from '../socialMedia-C05Iy-SV.js';
3
3
  import 'payload';
4
4
  import '../sitePreviewTypes-BrJwGzJj.js';
@@ -260,23 +260,33 @@ function configureAdmin(config) {
260
260
  if (cssSources.length === 0) {
261
261
  cssPath = sourceCssPath;
262
262
  } else {
263
- let css = cssSources.map((filePath) => import_fs.default.readFileSync(filePath, "utf-8")).join("\n\n");
264
- css = css.replace(
265
- "--orion-cms-brand-primary-fallback: #3b82f6;",
266
- `--orion-cms-brand-primary-fallback: ${brandPrimary};`
267
- );
268
- css = css.replace(
269
- "--orion-cms-brand-secondary-fallback: #8b5cf6;",
270
- `--orion-cms-brand-secondary-fallback: ${brandSecondary};`
271
- );
272
- const outputBasePath = config.basePath || process.cwd();
273
- const genDir = import_path.default.resolve(outputBasePath, ".generated");
274
- if (!import_fs.default.existsSync(genDir)) {
275
- import_fs.default.mkdirSync(genDir, { recursive: true });
263
+ try {
264
+ let css = cssSources.map((filePath) => import_fs.default.readFileSync(filePath, "utf-8")).join("\n\n");
265
+ css = css.replace(
266
+ "--orion-cms-brand-primary-fallback: #3b82f6;",
267
+ `--orion-cms-brand-primary-fallback: ${brandPrimary};`
268
+ );
269
+ css = css.replace(
270
+ "--orion-cms-brand-secondary-fallback: #8b5cf6;",
271
+ `--orion-cms-brand-secondary-fallback: ${brandSecondary};`
272
+ );
273
+ const outputBasePath = config.basePath || process.cwd();
274
+ const genDir = import_path.default.resolve(outputBasePath, ".generated");
275
+ const genPath = import_path.default.resolve(genDir, "admin.css");
276
+ let existing = null;
277
+ try {
278
+ existing = import_fs.default.readFileSync(genPath, "utf-8");
279
+ } catch {
280
+ existing = null;
281
+ }
282
+ if (existing !== css) {
283
+ import_fs.default.mkdirSync(genDir, { recursive: true });
284
+ import_fs.default.writeFileSync(genPath, css);
285
+ }
286
+ cssPath = genPath;
287
+ } catch {
288
+ cssPath = sourceCssPath;
276
289
  }
277
- const genPath = import_path.default.resolve(genDir, "admin.css");
278
- import_fs.default.writeFileSync(genPath, css);
279
- cssPath = genPath;
280
290
  }
281
291
  const clientPath = "@orion-studios/payload-studio/admin/client";
282
292
  const studioNavClientProps = {
@@ -729,16 +739,47 @@ function configureAdmin(config) {
729
739
  });
730
740
  },
731
741
  wrapGlobals(globals2) {
732
- const labelMap = {
742
+ const defaultLabelMap = {
733
743
  header: { group: "Site Design", label: "Header & Navigation" },
734
744
  footer: { group: "Site Design", label: "Footer" },
735
745
  "site-settings": { group: "Site Design", label: "Website Settings" },
736
746
  "social-media": { group: "Site Design", label: "Social Media" },
737
747
  "contact-form": { group: "Lead Forms", label: "Contact Form" }
738
748
  };
749
+ const configuredBySlug = new Map(
750
+ (config.studio?.globals || []).map((link) => [link.slug, link])
751
+ );
752
+ const attachBreadcrumbToGlobal = (global) => {
753
+ if (!studioEnabled) {
754
+ return global;
755
+ }
756
+ const existingControls = global.admin?.components?.elements?.beforeDocumentControls;
757
+ return {
758
+ ...global,
759
+ admin: {
760
+ ...global.admin,
761
+ components: {
762
+ ...global.admin?.components,
763
+ elements: {
764
+ ...global.admin?.components?.elements,
765
+ beforeDocumentControls: appendComponent(
766
+ existingControls,
767
+ studioBackBreadcrumbComponent,
768
+ "StudioBackBreadcrumb"
769
+ )
770
+ }
771
+ }
772
+ }
773
+ };
774
+ };
739
775
  return globals2.map((global) => {
740
- const mapping = labelMap[global.slug];
741
- if (!mapping) return global;
776
+ const configured = configuredBySlug.get(global.slug);
777
+ const defaults = defaultLabelMap[global.slug];
778
+ const mapping = configured || defaults ? {
779
+ group: configured?.group || defaults?.group,
780
+ label: configured?.label || defaults?.label || global.slug
781
+ } : void 0;
782
+ if (!mapping) return attachBreadcrumbToGlobal(global);
742
783
  const shouldAttachSiteSettingsEditView = studioEnabled && global.slug === "site-settings";
743
784
  const shouldAttachSocialMediaEditView = studioEnabled && global.slug === "social-media";
744
785
  const shouldAttachContactFormRedirect = studioEnabled && global.slug === "contact-form";
@@ -847,7 +888,7 @@ function configureAdmin(config) {
847
888
  ...global,
848
889
  admin: {
849
890
  ...global.admin,
850
- group: mapping.group,
891
+ ...mapping.group ? { group: mapping.group } : {},
851
892
  components: {
852
893
  ...global.admin?.components,
853
894
  elements: {
@@ -7,7 +7,7 @@ import {
7
7
  socialMediaConnectionsField,
8
8
  themePreferenceField,
9
9
  withTooltips
10
- } from "../chunk-KHK6RTGC.mjs";
10
+ } from "../chunk-MMJNHBOF.mjs";
11
11
  import {
12
12
  SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM,
13
13
  SOCIAL_MEDIA_ICON_OPTIONS,
@@ -17,8 +17,7 @@ import {
17
17
  defaultPageLayoutBlocks,
18
18
  sectionPresets,
19
19
  templateStarterPresets
20
- } from "../chunk-JQAHXYAM.mjs";
21
- import "../chunk-OQSEJXC4.mjs";
20
+ } from "../chunk-KMYMSR7W.mjs";
22
21
  import "../chunk-6BWS3CLP.mjs";
23
22
  export {
24
23
  BeforeAfterBlock,