@myissue/vue-website-page-builder 3.3.55 → 3.3.57

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.
@@ -21,6 +21,7 @@ import type { ComputedRef } from 'vue'
21
21
  import { v4 as uuidv4 } from 'uuid'
22
22
  import { delay } from './delay'
23
23
  import { isEmptyObject } from '../helpers/isEmptyObject'
24
+ import { extractCleanHTMLFromPageBuilder } from './extractCleanHTMLFromPageBuilder'
24
25
 
25
26
  export class PageBuilderService {
26
27
  // Class properties with types
@@ -245,79 +246,6 @@ export class PageBuilderService {
245
246
  }
246
247
  }
247
248
 
248
- #handlePageBuilderNotPresent(passedDataComponents: BuilderResourceData) {
249
- this.pendingMountData = passedDataComponents
250
- }
251
-
252
- async #mountPassedComponentsToDOM(components?: BuilderResourceData): Promise<void> {
253
- const config = this.pageBuilderStateStore.getPageBuilderConfig
254
- const formType = config && config.updateOrCreate && config.updateOrCreate.formType
255
- const localStorageData = this.loadStoredComponentsFromStorage()
256
-
257
- let dataToPass: string
258
- if (typeof components === 'string') {
259
- dataToPass = components
260
- } else if (components !== undefined) {
261
- dataToPass = JSON.stringify(components)
262
- } else {
263
- dataToPass = ''
264
- }
265
-
266
- await this.#updateComponentsFromString(dataToPass)
267
- }
268
-
269
- async tryMountPendingComponents() {
270
- this.pageBuilderStateStore.setIsLoadingGlobal(true)
271
- await delay(400)
272
-
273
- // Always clear DOM and store before mounting new resource
274
- this.deleteAllComponentsFromDOM()
275
- const localStorageData = this.loadStoredComponentsFromStorage()
276
-
277
- const config = this.pageBuilderStateStore.getPageBuilderConfig
278
- const formType = config && config.updateOrCreate && config.updateOrCreate.formType
279
-
280
- //
281
- if (config) {
282
- if (formType === 'update') {
283
- //
284
- if (localStorageData && typeof localStorageData === 'string') {
285
- this.pageBuilderStateStore.setHasLocalDraftForUpdate(true)
286
- }
287
-
288
- if (this.pendingMountData) {
289
- this.#completeBuilderInitialization(this.pendingMountData)
290
- }
291
-
292
- // Pending data for mount is null at this stage
293
- if (typeof localStorageData === 'string' && !this.pendingMountData) {
294
- await this.#updateComponentsFromString(localStorageData)
295
- this.#completeBuilderInitialization()
296
- }
297
- }
298
-
299
- if (formType === 'create') {
300
- // Pending data for mount is null at this stage
301
- if (typeof localStorageData === 'string') {
302
- await this.#updateComponentsFromString(localStorageData)
303
- this.#completeBuilderInitialization()
304
- }
305
- }
306
-
307
- //
308
- //
309
- //
310
- //
311
- //
312
- // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
313
- await nextTick()
314
- // Attach event listeners to all editable elements in the Builder
315
- await this.#addListenersToEditableElements()
316
-
317
- this.pageBuilderStateStore.setIsRestoring(false)
318
- this.pageBuilderStateStore.setIsLoadingGlobal(false)
319
- }
320
- }
321
249
  /**
322
250
  * - Entry point for initializing the Page Builder.
323
251
  * - Sets the builder as started in the state store.
@@ -351,11 +279,11 @@ export class PageBuilderService {
351
279
 
352
280
  // Page Builder is not Present in the DOM but Components have been passed to the Builder
353
281
  if (passedComponentsArray && !pagebuilder) {
354
- this.#handlePageBuilderNotPresent(passedComponentsArray)
282
+ this.pendingMountData = passedComponentsArray
355
283
  }
356
284
  // Page Builder is Present in the DOM & Components have been passed to the Builder
357
285
  if (pagebuilder) {
358
- this.#completeBuilderInitialization(passedComponentsArray)
286
+ this.completeBuilderInitialization(passedComponentsArray)
359
287
  }
360
288
 
361
289
  // result to end user
@@ -385,45 +313,90 @@ export class PageBuilderService {
385
313
  }
386
314
  }
387
315
 
388
- async #completeBuilderInitialization(passedComponentsArray?: BuilderResourceData): Promise<void> {
316
+ async completeBuilderInitialization(passedComponentsArray?: BuilderResourceData): Promise<void> {
317
+ this.pageBuilderStateStore.setIsLoadingGlobal(true)
318
+ await delay(400)
319
+
320
+ // Always clear DOM and store before mounting new resource
321
+ this.deleteAllComponentsFromDOM()
322
+
323
+ const config = this.pageBuilderStateStore.getPageBuilderConfig
324
+ const formType = config && config.updateOrCreate && config.updateOrCreate.formType
325
+
389
326
  const localStorageData = this.loadStoredComponentsFromStorage()
390
327
 
391
328
  // Deselect any selected or hovered elements in the builder UI
392
329
  await this.clearHtmlSelection()
393
330
 
394
- if (passedComponentsArray) {
395
- // Prefer components from local storage if available for this resource
396
- if (!this.pendingMountData && localStorageData && typeof localStorageData === 'string') {
397
- await this.#updateComponentsFromString(localStorageData)
398
- } else {
399
- // If no local storage is found, use the components array provided by the user
400
- await this.#mountPassedComponentsToDOM(passedComponentsArray)
401
- this.pendingMountData = null
402
- }
403
- }
404
-
405
331
  //
406
332
  //
407
333
  //
408
- if (!passedComponentsArray) {
409
- // Prefer components from local storage if available for this resource
410
- if (localStorageData && typeof localStorageData === 'string') {
411
- await this.#updateComponentsFromString(localStorageData)
412
- } else {
413
- // If no local storage is found, use the components array provided by the user
414
- await this.#mountPassedComponentsToDOM([])
334
+ if (formType === 'update' || formType === 'create') {
335
+ if (!this.pendingMountData) {
336
+ // FOCUS ON: passedComponentsArray
337
+ if (passedComponentsArray && !localStorageData) {
338
+ await this.#completeMountProcess(JSON.stringify(passedComponentsArray))
339
+ return
340
+ }
341
+ if (passedComponentsArray && localStorageData) {
342
+ this.pageBuilderStateStore.setHasLocalDraftForUpdate(true)
343
+
344
+ await this.#completeMountProcess(JSON.stringify(passedComponentsArray))
345
+ return
346
+ }
347
+
348
+ if (passedComponentsArray && localStorageData) {
349
+ this.pageBuilderStateStore.setHasLocalDraftForUpdate(true)
350
+
351
+ await this.#completeMountProcess(JSON.stringify(passedComponentsArray))
352
+ return
353
+ }
354
+
355
+ if (localStorageData && !passedComponentsArray) {
356
+ await this.#completeMountProcess(localStorageData)
357
+ return
358
+ }
359
+ if (!passedComponentsArray && !localStorageData) {
360
+ await this.#completeMountProcess(JSON.stringify([]))
361
+ return
362
+ }
363
+ }
364
+
365
+ // FOCUS ON: pendingMountData
366
+ if (this.pendingMountData) {
367
+ if (localStorageData) {
368
+ this.pageBuilderStateStore.setHasLocalDraftForUpdate(true)
369
+ await this.#completeMountProcess(JSON.stringify(this.pendingMountData))
370
+ this.pendingMountData = null
371
+ return
372
+ }
373
+ if (!localStorageData && passedComponentsArray) {
374
+ await this.#completeMountProcess(JSON.stringify(this.pendingMountData))
375
+ this.pendingMountData = null
376
+ return
377
+ }
378
+
379
+ if (!passedComponentsArray && !localStorageData) {
380
+ await this.#completeMountProcess(JSON.stringify([]))
381
+ return
382
+ }
415
383
  }
416
384
  }
417
385
  //
386
+ }
418
387
 
388
+ async #completeMountProcess(html: string) {
389
+ await this.#mountComponentsToDOM(html)
419
390
  // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
420
391
  await nextTick()
421
392
  // Attach event listeners to all editable elements in the Builder
422
393
  await this.#addListenersToEditableElements()
423
- // Show a global loading indicator while initializing
424
394
 
425
395
  // Clean up any old localStorage items related to previous builder sessions
426
396
  this.deleteOldPageBuilderLocalStorage()
397
+
398
+ this.pageBuilderStateStore.setIsRestoring(false)
399
+ this.pageBuilderStateStore.setIsLoadingGlobal(false)
427
400
  }
428
401
 
429
402
  #applyElementClassChanges(
@@ -476,6 +449,22 @@ export class PageBuilderService {
476
449
  return currentCSS
477
450
  }
478
451
 
452
+ async globalPageStyles() {
453
+ const pagebuilder = document.querySelector('#pagebuilder')
454
+ if (!pagebuilder) return
455
+
456
+ // Deselect any selected or hovered elements in the builder UI
457
+ await this.clearHtmlSelection()
458
+ //
459
+ // Set the element in the store
460
+ this.pageBuilderStateStore.setElement(pagebuilder as HTMLElement)
461
+
462
+ // Add the data attribute for styling
463
+ pagebuilder.setAttribute('data-global-selected', 'true')
464
+
465
+ await nextTick()
466
+ }
467
+
479
468
  handleFontWeight(userSelectedFontWeight?: string): void {
480
469
  this.#applyElementClassChanges(
481
470
  userSelectedFontWeight,
@@ -860,8 +849,6 @@ export class PageBuilderService {
860
849
  // Add prefix if missing
861
850
  const prefixedClass = cleanedClass.startsWith('pbx-') ? cleanedClass : 'pbx-' + cleanedClass
862
851
 
863
- console.log('Adding class:', prefixedClass)
864
-
865
852
  this.getElement.value?.classList.add(prefixedClass)
866
853
 
867
854
  this.pageBuilderStateStore.setElement(this.getElement.value)
@@ -1275,40 +1262,21 @@ export class PageBuilderService {
1275
1262
  const pagebuilder = document.querySelector('#pagebuilder')
1276
1263
  if (!pagebuilder) return
1277
1264
 
1278
- const addedHtmlComponents = ref<string[]>([])
1279
- // preview current design in external browser tab
1280
- // iterate over each top-level section component within pagebuilder only
1281
- pagebuilder.querySelectorAll('section:not(section section)').forEach((section) => {
1282
- // remove hovered and selected
1283
-
1284
- // remove hovered
1285
- const hoveredElement = section.querySelector('[hovered]')
1286
- if (hoveredElement) {
1287
- hoveredElement.removeAttribute('hovered')
1288
- }
1289
-
1290
- // remove selected
1291
- const selectedElement = section.querySelector('[selected]')
1292
- if (selectedElement) {
1293
- selectedElement.removeAttribute('selected')
1294
- }
1295
-
1296
- // push outer html into the array
1297
- addedHtmlComponents.value.push(section.outerHTML)
1298
- })
1299
-
1300
- // stringify added html components
1301
- const stringifiedComponents = JSON.stringify(addedHtmlComponents.value)
1302
-
1303
- // commit
1304
- this.pageBuilderStateStore.setCurrentLayoutPreview(stringifiedComponents)
1265
+ if (pagebuilder) {
1266
+ // Get cleaned HTML from entire builder
1267
+ const cleanedHTML = extractCleanHTMLFromPageBuilder(
1268
+ pagebuilder as HTMLElement,
1269
+ this.pageBuilderStateStore.getPageBuilderConfig
1270
+ ? this.pageBuilderStateStore.getPageBuilderConfig
1271
+ : undefined,
1272
+ )
1305
1273
 
1306
- // set added html components back to empty array
1307
- addedHtmlComponents.value = []
1274
+ // Store as array with one string (as your preview expects an array)
1275
+ const previewData = JSON.stringify([cleanedHTML])
1308
1276
 
1309
- //
1277
+ this.pageBuilderStateStore.setCurrentLayoutPreview(previewData)
1278
+ }
1310
1279
  }
1311
-
1312
1280
  // Helper function to sanitize title for localStorage key
1313
1281
  private sanitizeForLocalStorage(input: string): string {
1314
1282
  return input
@@ -1534,10 +1502,15 @@ export class PageBuilderService {
1534
1502
  })
1535
1503
  })
1536
1504
 
1537
- // Save to localStorage with pageBuilderContentSavedAt using the correct key
1505
+ const pageSettings = {
1506
+ classes: pagebuilder.className || '',
1507
+ style: pagebuilder.getAttribute('style') || '',
1508
+ }
1509
+
1538
1510
  const dataToSave = {
1539
1511
  components: componentsToSave,
1540
1512
  pageBuilderContentSavedAt: new Date().toISOString(),
1513
+ pageSettings,
1541
1514
  }
1542
1515
 
1543
1516
  const keyForSavingFromDomToLocal = this.getLocalStorageItemName.value
@@ -1546,7 +1519,6 @@ export class PageBuilderService {
1546
1519
  localStorage.setItem(keyForSavingFromDomToLocal, JSON.stringify(dataToSave))
1547
1520
  }
1548
1521
  }
1549
-
1550
1522
  async removeCurrentComponentsFromLocalStorage() {
1551
1523
  this.#updateLocalStorageItemName()
1552
1524
  await nextTick()
@@ -1616,12 +1588,11 @@ export class PageBuilderService {
1616
1588
  }
1617
1589
 
1618
1590
  //
1619
- async resumeEditingForUpdate() {
1591
+ async resumeEditingFromDraft() {
1620
1592
  this.#updateLocalStorageItemName()
1621
1593
  const config = this.pageBuilderStateStore.getPageBuilderConfig
1622
1594
  const formType = config && config.updateOrCreate && config.updateOrCreate.formType
1623
1595
 
1624
- if (formType !== 'update') return
1625
1596
  //
1626
1597
  //
1627
1598
  //
@@ -1634,7 +1605,7 @@ export class PageBuilderService {
1634
1605
  if (typeof updateDraftFromLocalStorage === 'string') {
1635
1606
  this.pageBuilderStateStore.setIsLoadingResumeEditing(true)
1636
1607
  await delay(400)
1637
- await this.#updateComponentsFromString(updateDraftFromLocalStorage)
1608
+ await this.#mountComponentsToDOM(updateDraftFromLocalStorage)
1638
1609
  this.pageBuilderStateStore.setIsLoadingResumeEditing(false)
1639
1610
  }
1640
1611
  }
@@ -1658,7 +1629,7 @@ export class PageBuilderService {
1658
1629
 
1659
1630
  // Restore the original content if available
1660
1631
  if (Array.isArray(this.originalComponents)) {
1661
- await this.#mountPassedComponentsToDOM(this.originalComponents)
1632
+ await this.#mountComponentsToDOM(JSON.stringify(this.originalComponents))
1662
1633
  this.removeCurrentComponentsFromLocalStorage()
1663
1634
  }
1664
1635
 
@@ -1973,6 +1944,20 @@ export class PageBuilderService {
1973
1944
  .join(' ')
1974
1945
  }
1975
1946
 
1947
+ #convertStyleObjectToString(
1948
+ styleObj: string | Record<string, string> | null | undefined,
1949
+ ): string {
1950
+ if (!styleObj) return ''
1951
+ if (typeof styleObj === 'string') return styleObj
1952
+
1953
+ return Object.entries(styleObj)
1954
+ .map(([key, value]) => {
1955
+ const kebabKey = key.replace(/([A-Z])/g, '-$1').toLowerCase()
1956
+ return `${kebabKey}: ${value};`
1957
+ })
1958
+ .join(' ')
1959
+ }
1960
+
1976
1961
  /**
1977
1962
  * Parse and set components from JSON or HTML data
1978
1963
  *
@@ -1985,7 +1970,7 @@ export class PageBuilderService {
1985
1970
  * @param data - JSON string (e.g., '[{"html_code":"...","id":"123","title":"..."}]')
1986
1971
  * OR HTML string (e.g., '<section data-componentid="123">...</section>')
1987
1972
  */
1988
- async #updateComponentsFromString(htmlString: string): Promise<void> {
1973
+ async #mountComponentsToDOM(htmlString: string): Promise<void> {
1989
1974
  // Auto-detect if input is JSON or HTML
1990
1975
  const trimmedData = htmlString.trim()
1991
1976
 
@@ -2003,11 +1988,27 @@ export class PageBuilderService {
2003
1988
  await this.#parseJSONComponents(trimmedData)
2004
1989
  }
2005
1990
 
1991
+ // Private method to parse JSON components and save pageBuilderContentSavedAt to localStorage
2006
1992
  // Private method to parse JSON components and save pageBuilderContentSavedAt to localStorage
2007
1993
  async #parseJSONComponents(jsonData: string): Promise<void> {
2008
1994
  try {
2009
1995
  const parsedData = JSON.parse(jsonData)
2010
1996
  let componentsArray: ComponentObject[] = []
1997
+
1998
+ // Fallback to store-based config if not provided in imported JSON
1999
+ const pageSettings =
2000
+ this.pageBuilderStateStore.getPageBuilderConfig &&
2001
+ this.pageBuilderStateStore.getPageBuilderConfig.pageSettings
2002
+
2003
+ // Restore page-level settings like class and style
2004
+ if (pageSettings) {
2005
+ const pagebuilder = document.querySelector('#pagebuilder') as HTMLElement
2006
+ if (pagebuilder) {
2007
+ pagebuilder.className = pageSettings.classes || ''
2008
+ pagebuilder.setAttribute('style', this.#convertStyleObjectToString(pageSettings.style))
2009
+ }
2010
+ }
2011
+
2011
2012
  // Support both old and new structure
2012
2013
  if (Array.isArray(parsedData)) {
2013
2014
  componentsArray = parsedData
@@ -2024,7 +2025,7 @@ export class PageBuilderService {
2024
2025
  const section = doc.querySelector('section')
2025
2026
 
2026
2027
  if (section) {
2027
- // Process all elements inside section to add prefix to classes
2028
+ // Prefix Tailwind classes
2028
2029
  section.querySelectorAll('[class]').forEach((el) => {
2029
2030
  el.setAttribute(
2030
2031
  'class',
@@ -2032,7 +2033,7 @@ export class PageBuilderService {
2032
2033
  )
2033
2034
  })
2034
2035
 
2035
- // Ensure data-componentid exists
2036
+ // Ensure IDs & titles
2036
2037
  if (!section.hasAttribute('data-componentid')) {
2037
2038
  const newId = uuidv4()
2038
2039
  section.setAttribute('data-componentid', newId)
@@ -2041,24 +2042,20 @@ export class PageBuilderService {
2041
2042
  component.id = section.getAttribute('data-componentid')!
2042
2043
  }
2043
2044
 
2044
- // Ensure data-component-title exists
2045
2045
  const title = component.title || 'Untitled Component'
2046
2046
  section.setAttribute('data-component-title', title)
2047
2047
  component.title = title
2048
2048
 
2049
- // Update html_code with modified section
2049
+ // Update html_code
2050
2050
  component.html_code = section.outerHTML
2051
2051
  }
2052
-
2053
2052
  return component
2054
2053
  })
2055
2054
  }
2056
2055
 
2057
2056
  this.pageBuilderStateStore.setComponents(savedCurrentDesign)
2058
2057
 
2059
- // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
2060
2058
  await nextTick()
2061
- // Attach event listeners to all editable elements in the Builder
2062
2059
  await this.#addListenersToEditableElements()
2063
2060
  } catch (error) {
2064
2061
  console.error('Error parsing JSON components:', error)
@@ -2071,12 +2068,37 @@ export class PageBuilderService {
2071
2068
  const parser = new DOMParser()
2072
2069
  const doc = parser.parseFromString(htmlData, 'text/html')
2073
2070
 
2074
- // Select all <section> elements (with or without data-componentid)
2071
+ const importedPageBuilder = doc.querySelector('#pagebuilder') as HTMLElement | null
2072
+ const livePageBuilder = document.querySelector('#pagebuilder') as HTMLElement | null
2073
+
2074
+ if (livePageBuilder) {
2075
+ if (importedPageBuilder) {
2076
+ livePageBuilder.className = importedPageBuilder.className || ''
2077
+ const style = importedPageBuilder.getAttribute('style')
2078
+ if (style !== null) {
2079
+ livePageBuilder.setAttribute('style', style)
2080
+ } else {
2081
+ livePageBuilder.removeAttribute('style')
2082
+ }
2083
+ } else {
2084
+ // Fallback: inject settings from store if not present in HTML
2085
+ const fallbackSettings = this.pageBuilderStateStore.getPageBuilderConfig?.pageSettings
2086
+ if (fallbackSettings) {
2087
+ livePageBuilder.className = fallbackSettings.classes || ''
2088
+ livePageBuilder.setAttribute(
2089
+ 'style',
2090
+ this.#convertStyleObjectToString(pageSettings.style),
2091
+ )
2092
+ }
2093
+ }
2094
+ }
2095
+
2096
+ // Select all <section> elements
2075
2097
  const sectionElements = doc.querySelectorAll('section')
2076
2098
 
2077
2099
  const extractedSections: ComponentObject[] = []
2078
2100
  sectionElements.forEach((section) => {
2079
- // Process all elements inside section to add prefix to classes
2101
+ // Prefix all classes inside section
2080
2102
  section.querySelectorAll('[class]').forEach((el) => {
2081
2103
  el.setAttribute(
2082
2104
  'class',
@@ -2109,11 +2131,9 @@ export class PageBuilderService {
2109
2131
 
2110
2132
  this.pageBuilderStateStore.setComponents(extractedSections)
2111
2133
 
2112
- // Deselect any selected or hovered elements in the builder UI
2134
+ // Clear selections and re-bind events
2113
2135
  await this.clearHtmlSelection()
2114
- // Wait for Vue to finish DOM updates before attaching event listeners. This ensure elements exist in the DOM.
2115
2136
  await nextTick()
2116
- // Attach event listeners to all editable elements in the Builder
2117
2137
  await this.#addListenersToEditableElements()
2118
2138
  } catch (error) {
2119
2139
  console.error('Error parsing HTML components:', error)
@@ -0,0 +1,58 @@
1
+ import type { PageBuilderConfig } from '@/types'
2
+
3
+ export function extractCleanHTMLFromPageBuilder(
4
+ pagebuilder: HTMLElement | null,
5
+ config?: PageBuilderConfig,
6
+ ): string {
7
+ if (!pagebuilder) {
8
+ console.warn('No valid pagebuilder element passed')
9
+ return ''
10
+ }
11
+
12
+ const clone = pagebuilder.cloneNode(true) as HTMLElement
13
+ clone.removeAttribute('id')
14
+
15
+ // Remove custom attributes
16
+ const elementsWithAttrs = clone.querySelectorAll<HTMLElement>(
17
+ '[data-componentid], [data-component-title], #page-builder-editor-editable-area',
18
+ )
19
+
20
+ elementsWithAttrs.forEach((el) => {
21
+ el.removeAttribute('data-componentid')
22
+ el.removeAttribute('data-component-title')
23
+ if (el.id === 'page-builder-editor-editable-area') {
24
+ el.removeAttribute('id')
25
+ }
26
+ })
27
+
28
+ if (config && config.pageSettings && typeof config.pageSettings.imageUrlPrefix === 'string') {
29
+ const imageUrlPrefix = config.pageSettings.imageUrlPrefix
30
+ const imgs = clone.querySelectorAll<HTMLImageElement>('img')
31
+ imgs.forEach((img) => {
32
+ const src = img.getAttribute('src') || ''
33
+ if (
34
+ !src.startsWith('http') &&
35
+ imageUrlPrefix && // extra safety
36
+ !src.startsWith(imageUrlPrefix)
37
+ ) {
38
+ img.setAttribute('src', imageUrlPrefix + src.replace(/^\/+/, ''))
39
+ }
40
+ })
41
+ }
42
+
43
+ // Recursively remove all comment nodes
44
+ const removeComments = (node: Node): void => {
45
+ for (let i = node.childNodes.length - 1; i >= 0; i--) {
46
+ const child = node.childNodes[i]
47
+ if (child.nodeType === Node.COMMENT_NODE) {
48
+ node.removeChild(child)
49
+ } else if (child.nodeType === Node.ELEMENT_NODE) {
50
+ removeComments(child)
51
+ }
52
+ }
53
+ }
54
+
55
+ removeComments(clone)
56
+
57
+ return clone.outerHTML
58
+ }
package/src/css/app.css CHANGED
@@ -550,3 +550,10 @@
550
550
  border-radius: 25px;
551
551
  border: none;
552
552
  }
553
+
554
+ #pagebuilder[data-global-selected] {
555
+ outline: 3px dashed #b91010 !important;
556
+ outline-offset: -2px !important;
557
+ box-shadow: 0 0 0 2px #fbbf24;
558
+ /* Or any style you want */
559
+ }
@@ -201,6 +201,12 @@ export interface PageBuilderUser {
201
201
  image: string
202
202
  }
203
203
 
204
+ export interface PageSettings {
205
+ classes: string
206
+ style?: Record<string, string>
207
+ imageUrlPrefix?: string
208
+ }
209
+
204
210
  // Page Builder Configuration interface
205
211
  export interface PageBuilderConfig {
206
212
  updateOrCreate: {
@@ -209,7 +215,7 @@ export interface PageBuilderConfig {
209
215
  }
210
216
  pageBuilderLogo?: { src: string } | null
211
217
  resourceData?: { title: string; id?: number } | null
212
- userForPageBuilder?: { name: string } | null
218
+ userForPageBuilder?: PageBuilderUser
213
219
  [key: string]: unknown
214
220
  userSettings?: {
215
221
  theme?: 'light' | 'dark' | 'auto'
@@ -221,6 +227,7 @@ export interface PageBuilderConfig {
221
227
  brandColor?: string
222
228
  [key: string]: unknown
223
229
  } | null
230
+ pageSettings?: PageSettings
224
231
  }
225
232
  // Tailwind utility interfaces
226
233
  export interface TailwindColors {