@designcrowd/library.brand-page 6.0.11 → 6.0.13

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 (117) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-DopPc6g2.js → CreatedOnBrandCrowd-de-de-CBjFFeBP.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-CSHmaAWm.js → CreatedOnBrandCrowd-en-us-NuNXF81K.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-C4vM0p2e.js → CreatedOnBrandCrowd-es-es-BJG8nfug.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-ArpBbnxm.js → CreatedOnBrandCrowd-fr-ca-uX-nL53e.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-ArpBbnxm.js → CreatedOnBrandCrowd-fr-fr-uX-nL53e.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-CGAQLXiX.js → CreatedOnBrandCrowd-pt-br-DTrgFGQV.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-CGAQLXiX.js → CreatedOnBrandCrowd-pt-pt-DTrgFGQV.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-3WVSgB6t.js → CreatedOnDcom-de-de-DSfpxioT.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-Dx-RHXrH.js → CreatedOnDcom-en-us-C7G4gKem.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-CfnEYFMA.js → CreatedOnDcom-es-es-DVckCaji.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-BSHZTiUO.js → CreatedOnDcom-fr-ca-C5GDrm_C.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-DtHssrQb.js → CreatedOnDcom-fr-fr-v5eMm-j2.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-CdX1aRI8.js → CreatedOnDcom-pt-br-Dq3-fPUc.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-jy044FNL.js → CreatedOnDcom-pt-pt-DFsncYax.js} +1 -1
  15. package/lib/src/brand-page/components/image-gallery/ImageGallery.stories.js +3 -0
  16. package/lib/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.d.ts +1 -1
  17. package/lib/src/brand-page/components/images-section/ImagesSection.stories.js +2 -1
  18. package/lib/src/brand-page/components/social-links/SocialLinks.stories.js +3 -0
  19. package/lib/src/brand-page/components/testimonial-section/__fixtures__/data.js +3 -0
  20. package/lib/src/brand-page/components/text/Text.stories.js +2 -0
  21. package/lib/src/brand-page/models/index.d.ts +13 -0
  22. package/lib/src/brand-page/models/index.js +4 -0
  23. package/lib/src/brand-page/utils/migration/migration-01.js +2 -0
  24. package/lib/src/brand-page/utils/migration/migration-18.d.ts +14 -0
  25. package/lib/src/brand-page/utils/migration/migration-18.js +17 -0
  26. package/lib/src/brand-page/utils/migration.util.js +6 -0
  27. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.js +1 -0
  28. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +1 -0
  29. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +5 -0
  30. package/lib/src/brand-page-maker/constants/config-settings-constant.js +4 -0
  31. package/lib/src/brand-page-maker/models/content-settings.js +4 -0
  32. package/lib/src/brand-page-maker/stores/ai-checkpoint/ai-checkpoint-store.d.ts +18 -0
  33. package/lib/src/index.mjs +84 -83
  34. package/lib/src/{lib-Bx7xK4dn.js → lib-BG-pyXi9.js} +19585 -19807
  35. package/lib/src/metadata-builder/ai-metadata-executor.js +24 -1
  36. package/lib/src/metadata-builder/builders/footer-builder.js +32 -41
  37. package/lib/src/metadata-builder/builders/item-id-utils.d.ts +54 -0
  38. package/lib/src/metadata-builder/builders/item-id-utils.js +96 -0
  39. package/lib/src/metadata-builder/builders/move-page-builder.d.ts +32 -0
  40. package/lib/src/metadata-builder/builders/move-page-builder.js +137 -0
  41. package/lib/src/metadata-builder/builders/nav-utils.d.ts +26 -0
  42. package/lib/src/metadata-builder/builders/nav-utils.js +95 -0
  43. package/lib/src/metadata-builder/builders/section-builders/image-gallery-builder.js +57 -68
  44. package/lib/src/metadata-builder/builders/section-builders/images-section-builder.js +58 -93
  45. package/lib/src/metadata-builder/builders/section-builders/move-section-builder.d.ts +13 -1
  46. package/lib/src/metadata-builder/builders/section-builders/move-section-builder.js +40 -30
  47. package/lib/src/metadata-builder/builders/section-builders/shapes-section-builder.js +66 -99
  48. package/lib/src/metadata-builder/builders/section-builders/social-links-builder.js +57 -17
  49. package/lib/src/metadata-builder/builders/section-builders/testimonial-section-builder.js +51 -85
  50. package/lib/src/metadata-builder/builders/section-builders/text-section-builder.js +50 -74
  51. package/lib/src/metadata-builder/builders/section-field-maps.js +17 -0
  52. package/lib/src/metadata-builder/builders/set-page-visibility-builder.d.ts +23 -0
  53. package/lib/src/metadata-builder/builders/set-page-visibility-builder.js +83 -0
  54. package/lib/src/metadata-builder/metadata-executor.js +4 -0
  55. package/lib/src/metadata-builder/models.d.ts +2 -0
  56. package/lib/src/metadata-builder/models.js +2 -0
  57. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.d.ts +31 -7
  58. package/lib/src/metadata-builder/parsers/ai-toolcall-parser.js +2 -0
  59. package/lib/src/metadata-builder/parsers/section-tool-arguments.d.ts +84 -48
  60. package/lib/src/shared/content-settings/components/links-field/LinksField.mixin.js +2 -0
  61. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +2 -0
  62. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +2 -0
  63. package/lib/src/tracker.d.ts +2 -0
  64. package/lib/src/tracker.js +12 -0
  65. package/lib/src/tracker.mjs +12 -0
  66. package/lib/src/useEventTracker-DmQ6AkNn.js +356 -0
  67. package/lib/src/utils/minify-brand-page-metadata.d.ts +2 -0
  68. package/lib/src/utils/minify-brand-page-metadata.js +8 -1
  69. package/package.json +16 -1
  70. package/src/brand-page/components/image-gallery/ImageGallery.stories.ts +3 -0
  71. package/src/brand-page/components/image-gallery/ImageGallery.test.ts +1 -0
  72. package/src/brand-page/components/image-gallery/masonry/MasonryGallery.mixin.ts +2 -1
  73. package/src/brand-page/components/images-section/ImagesSection.stories.ts +2 -1
  74. package/src/brand-page/components/social-links/SocialLinks.stories.ts +3 -0
  75. package/src/brand-page/components/social-links/SocialLinks.vue.test.ts +5 -0
  76. package/src/brand-page/components/testimonial-section/__fixtures__/data.ts +3 -0
  77. package/src/brand-page/components/text/Text.stories.ts +2 -0
  78. package/src/brand-page/models/index.ts +17 -0
  79. package/src/brand-page/utils/__snapshots__/migration.util.test.ts.snap +23 -3
  80. package/src/brand-page/utils/__snapshots__/migration2.util.test.ts.snap +6 -0
  81. package/src/brand-page/utils/__snapshots__/model.util.test.ts.snap +12 -0
  82. package/src/brand-page/utils/migration/migration-01.ts +2 -0
  83. package/src/brand-page/utils/migration/migration-18.test.ts +131 -0
  84. package/src/brand-page/utils/migration/migration-18.ts +22 -0
  85. package/src/brand-page/utils/migration.util.ts +7 -0
  86. package/src/brand-page/utils/personalisation.util.test.ts +4 -0
  87. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.stories.ts +1 -0
  88. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +1 -0
  89. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +6 -0
  90. package/src/brand-page-maker/constants/config-settings-constant.ts +4 -0
  91. package/src/brand-page-maker/models/content-settings.ts +4 -0
  92. package/src/metadata-builder/ai-metadata-executor.ts +31 -1
  93. package/src/metadata-builder/builders/footer-builder.test.ts +335 -28
  94. package/src/metadata-builder/builders/footer-builder.ts +40 -57
  95. package/src/metadata-builder/builders/item-id-utils.test.ts +184 -0
  96. package/src/metadata-builder/builders/item-id-utils.ts +117 -0
  97. package/src/metadata-builder/builders/move-page-builder.ts +227 -0
  98. package/src/metadata-builder/builders/nav-utils.ts +141 -0
  99. package/src/metadata-builder/builders/section-builders/image-gallery-builder.ts +62 -88
  100. package/src/metadata-builder/builders/section-builders/images-section-builder.ts +66 -129
  101. package/src/metadata-builder/builders/section-builders/move-section-builder.ts +58 -35
  102. package/src/metadata-builder/builders/section-builders/shapes-section-builder.ts +105 -160
  103. package/src/metadata-builder/builders/section-builders/social-links-builder.ts +65 -20
  104. package/src/metadata-builder/builders/section-builders/testimonial-section-builder.ts +83 -146
  105. package/src/metadata-builder/builders/section-builders/text-section-builder.ts +71 -122
  106. package/src/metadata-builder/builders/section-field-maps.ts +18 -0
  107. package/src/metadata-builder/builders/set-page-visibility-builder.ts +122 -0
  108. package/src/metadata-builder/metadata-executor.ts +4 -0
  109. package/src/metadata-builder/models.ts +2 -0
  110. package/src/metadata-builder/parsers/ai-toolcall-parser.ts +32 -7
  111. package/src/metadata-builder/parsers/section-tool-arguments.ts +84 -48
  112. package/src/shared/content-settings/components/links-field/LinksField.mixin.ts +2 -0
  113. package/src/tracker.ts +28 -0
  114. package/src/utils/minify-brand-page-metadata.ts +14 -2
  115. package/lib/src/metadata-builder/builders/reorder-pages-builder.d.ts +0 -13
  116. package/lib/src/metadata-builder/builders/reorder-pages-builder.js +0 -49
  117. package/src/metadata-builder/builders/reorder-pages-builder.ts +0 -80
@@ -0,0 +1,122 @@
1
+ import type { BrandPageModel, NavOrder } from '../../brand-page/models';
2
+ import {
3
+ BaseMetadataBuilder,
4
+ type BuilderResult,
5
+ type MetadataBuilderRequest,
6
+ } from '../models';
7
+ import type { Logger } from '../logger';
8
+ import { syncPagesWithNavOrders } from './nav-utils';
9
+
10
+ export interface SetPageVisibilityPayload {
11
+ /** The page whose visibility is toggled (resolved from page_id). */
12
+ pageId: string;
13
+ /** true = hide the page from the navigation; false = show it. */
14
+ isHidden: boolean;
15
+ }
16
+
17
+ /**
18
+ * Toggle a page's navigation visibility (the AI-chat equivalent of the page-settings
19
+ * "Hide page / Show page" menu). Sets `isHidden` on BOTH the matching `NavRoute`
20
+ * (in `model.nav.routes`) and the `PageModel` (in `model.pages`) — the two places the
21
+ * editor keeps in sync. No slug/order recompute (the nav order is unchanged); the
22
+ * published-site nav already filters hidden pages via `isHidden`.
23
+ */
24
+ export class SetPageVisibilityBuilder extends BaseMetadataBuilder<SetPageVisibilityPayload> {
25
+ constructor(
26
+ request: MetadataBuilderRequest<SetPageVisibilityPayload>,
27
+ logger?: Logger,
28
+ ) {
29
+ super(request, logger);
30
+ }
31
+
32
+ async validateAsync(): Promise<{ errors: string[] }> {
33
+ const errors: string[] = [];
34
+
35
+ if (!this.request.payload?.pageId) {
36
+ errors.push('pageId is required for set_page_visibility');
37
+ }
38
+
39
+ return { errors };
40
+ }
41
+
42
+ async buildAsync(model: BrandPageModel): Promise<BuilderResult> {
43
+ this.log('Build called');
44
+
45
+ const validation = await this.validateAsync();
46
+
47
+ if (validation.errors.length) {
48
+ return { request: this.request, model, errors: validation.errors };
49
+ }
50
+
51
+ const { pageId, isHidden } = this.request.payload!;
52
+
53
+ const targetRoute = model.nav.routes.find((r) => r.pageId === pageId);
54
+
55
+ if (!targetRoute) {
56
+ return {
57
+ request: this.request,
58
+ model,
59
+ errors: [`Page with ID "${pageId}" not found`],
60
+ };
61
+ }
62
+
63
+ // Guards apply only when HIDING; showing a page is always safe.
64
+ if (isHidden) {
65
+ // --- GUARD: the home page (orders[0]) cannot be hidden ---
66
+ const homeRouteId = model.nav.orders[0]?.routeId;
67
+
68
+ if (homeRouteId === targetRoute.id) {
69
+ return {
70
+ request: this.request,
71
+ model,
72
+ errors: [
73
+ `Home page "${pageId}" cannot be hidden from the navigation`,
74
+ ],
75
+ };
76
+ }
77
+
78
+ // --- GUARD: cannot hide the last remaining visible page ---
79
+ const visibleCount = model.nav.routes.filter((r) => !r.isHidden).length;
80
+
81
+ if (!targetRoute.isHidden && visibleCount <= 1) {
82
+ return {
83
+ request: this.request,
84
+ model,
85
+ errors: [
86
+ `Cannot hide "${pageId}" — at least one page must stay visible`,
87
+ ],
88
+ };
89
+ }
90
+ }
91
+
92
+ // Set on the NavRoute (drives the nav) and the PageModel (drives header page lists).
93
+ targetRoute.isHidden = isHidden;
94
+
95
+ const targetPage = model.pages.find((p) => p.id === pageId);
96
+
97
+ if (targetPage) {
98
+ targetPage.isHidden = isHidden;
99
+ }
100
+
101
+ // Match the editor's page-navigation store (reorderVisibleAndHiddenRoutes): after any
102
+ // visibility change, float visible pages to the top and sink hidden pages to the bottom
103
+ // of the top-level nav order, stable within each group. Top-level only — mirrors the
104
+ // editor, which does not re-partition nested subRoutes. `model.pages` is then re-synced
105
+ // to the new DFS order (same as MovePageBuilder).
106
+ const routeById = new Map(model.nav.routes.map((r) => [r.id, r]));
107
+ const isOrderHidden = (o: NavOrder) =>
108
+ Boolean(routeById.get(o.routeId)?.isHidden);
109
+ const visibleOrders = model.nav.orders.filter((o) => !isOrderHidden(o));
110
+ const hiddenOrders = model.nav.orders.filter((o) => isOrderHidden(o));
111
+
112
+ model.nav.orders = [...visibleOrders, ...hiddenOrders];
113
+
114
+ syncPagesWithNavOrders(model);
115
+
116
+ this.log({
117
+ message: `Set page "${pageId}" visibility — isHidden: ${isHidden}`,
118
+ });
119
+
120
+ return { request: this.request, model };
121
+ }
122
+ }
@@ -31,6 +31,8 @@ import {
31
31
  import { AddPageBuilder } from './builders/add-page-builder';
32
32
  import { RemovePageBuilder } from './builders/remove-page-builder';
33
33
  import { RenamePageBuilder } from './builders/rename-page-builder';
34
+ import { MovePageBuilder } from './builders/move-page-builder';
35
+ import { SetPageVisibilityBuilder } from './builders/set-page-visibility-builder';
34
36
  import { FooterBuilder } from './builders/footer-builder';
35
37
  import { GlobalStylesBuilder } from './builders/global-styles-builder';
36
38
  import { HeaderBuilder } from './builders/header-builder';
@@ -111,6 +113,8 @@ const getBuilderRegistry = (): Record<BuilderType, BuilderConstructor> => ({
111
113
  [BUILDER_TYPES.ADD_PAGE]: AddPageBuilder,
112
114
  [BUILDER_TYPES.REMOVE_PAGE]: RemovePageBuilder,
113
115
  [BUILDER_TYPES.RENAME_PAGE]: RenamePageBuilder,
116
+ [BUILDER_TYPES.MOVE_PAGE]: MovePageBuilder,
117
+ [BUILDER_TYPES.SET_PAGE_VISIBILITY]: SetPageVisibilityBuilder,
114
118
 
115
119
  // Footer operations
116
120
  [BUILDER_TYPES.UPDATE_FOOTER]: FooterBuilder,
@@ -68,6 +68,8 @@ export const BUILDER_TYPES = {
68
68
  ADD_PAGE: 'add-page',
69
69
  REMOVE_PAGE: 'remove-page',
70
70
  RENAME_PAGE: 'rename-page',
71
+ MOVE_PAGE: 'move-page',
72
+ SET_PAGE_VISIBILITY: 'set-page-visibility',
71
73
 
72
74
  // Footer operations
73
75
  UPDATE_FOOTER: 'update-footer',
@@ -52,6 +52,8 @@ export const AI_ASSISTANT_TOOL_NAMES = {
52
52
  ADD_PAGE: 'add_page',
53
53
  REMOVE_PAGE: 'remove_page',
54
54
  RENAME_PAGE: 'rename_page',
55
+ MOVE_PAGE: 'move_page',
56
+ SET_PAGE_VISIBILITY: 'set_page_visibility',
55
57
 
56
58
  // Footer operation tools
57
59
  UPDATE_FOOTER: 'update_footer',
@@ -169,7 +171,11 @@ export interface DeleteSectionArguments {
169
171
 
170
172
  export interface MoveSectionArguments {
171
173
  content_id: string;
172
- position: number;
174
+ /**
175
+ * Relative anchor on the destination page: the content_id to insert this section
176
+ * BEFORE (a page's first content_id moves it to the top). Omit to append to the end.
177
+ */
178
+ move_before_content_id?: string;
173
179
  target_page_id?: string;
174
180
  }
175
181
 
@@ -187,8 +193,18 @@ export interface RenamePageArguments {
187
193
  page_name: string;
188
194
  }
189
195
 
190
- export interface ReorderPagesArguments {
191
- page_order: string[];
196
+ export interface MovePageArguments {
197
+ page_id: string;
198
+ /** Destination parent page_id, or null/omitted for top-level. */
199
+ parent_page_id?: string | null;
200
+ /** Sibling anchor page_id to insert before, or "__end__"/omitted to append. */
201
+ move_before_page_id?: string | null;
202
+ }
203
+
204
+ export interface SetPageVisibilityArguments {
205
+ page_id: string;
206
+ /** true = hide the page from the navigation; false = show it. */
207
+ is_hidden: boolean;
192
208
  }
193
209
 
194
210
  /**
@@ -202,8 +218,17 @@ export interface ReorderPagesArguments {
202
218
  export interface UndoArguments {}
203
219
 
204
220
  export interface FooterSocialLinkItem {
205
- item_index?: number;
206
- insert_at?: number;
221
+ /**
222
+ * Stable id of the existing link to update or move. Omit to ADD a new link.
223
+ * Mirrors the section-level item_id convention (BP-5603).
224
+ */
225
+ item_id?: string;
226
+ /**
227
+ * Id of an existing link to position this link before (insert for new links,
228
+ * move for existing ones). Use "__end__" for the end of the list. Omit to
229
+ * leave an existing link in place / append a new link.
230
+ */
231
+ move_before_item_id?: string;
207
232
  social_link_icon?: string;
208
233
  url?: string;
209
234
  }
@@ -227,8 +252,8 @@ export interface UpdateFooterArguments {
227
252
  copyright_text_style?: TextStyleArguments;
228
253
  social_links?: FooterSocialLinkItem[];
229
254
  _replace_social_links?: boolean;
230
- _remove_social_link_indices?: number[];
231
- _reorder_social_links?: number[];
255
+ /** Stable ids of social links to remove. Order-independent (replaces index-based _remove_social_link_indices). */
256
+ _remove_social_link_ids?: string[];
232
257
  /** Desktop-only styling (display_style, background_color, icon_color, is_logo_visible, logo sizes). */
233
258
  desktop?: ViewportFooterOverrides;
234
259
  /** Mobile-only styling (display_style, background_color, icon_color, is_logo_visible, logo sizes). */
@@ -142,10 +142,15 @@ export interface ProfileSectionArguments {
142
142
  }
143
143
 
144
144
  export interface TextSectionItem {
145
- /** 0-based index of existing item to update in-place */
146
- item_index?: number;
147
- /** 0-based position to insert a new item at */
148
- insert_at?: number;
145
+ /** Stable id of the existing item to update or move. Omit to ADD a new item. */
146
+ item_id?: string;
147
+ /**
148
+ * Id of an existing item to position this item before (insert for new items,
149
+ * move for existing ones). Use "__end__" for the end of the list. Omit to
150
+ * leave an existing item in place / append a new item. Mirrors the
151
+ * section-level move_before_content_id.
152
+ */
153
+ move_before_item_id?: string;
149
154
  text?: RichTextField;
150
155
  text_style?: TextStyleArguments;
151
156
  }
@@ -159,11 +164,11 @@ export interface TextSectionArguments {
159
164
  /** Multiple text items (BrandSite) */
160
165
  items?: TextSectionItem[];
161
166
  _replace_items?: boolean;
162
- /** 0-based indices of items to remove */
163
- _remove_item_indices?: number[];
164
- /** New order as array of current 0-based indices */
165
- _reorder_items?: number[];
167
+ /** Stable ids of items to remove. Order-independent (no index shifting). */
168
+ _remove_item_ids?: string[];
166
169
  background_color?: string | null;
170
+ padding_top?: number;
171
+ padding_bottom?: number;
167
172
  }
168
173
 
169
174
  export interface ImageSectionArguments {
@@ -172,10 +177,15 @@ export interface ImageSectionArguments {
172
177
  }
173
178
 
174
179
  export interface ImageGalleryItem {
175
- /** 0-based index of existing item to update in-place */
176
- item_index?: number;
177
- /** 0-based position to insert a new item at */
178
- insert_at?: number;
180
+ /** Stable id of the existing item to update or move. Omit to ADD a new item. */
181
+ item_id?: string;
182
+ /**
183
+ * Id of an existing item to position this item before (insert for new items,
184
+ * move for existing ones). Use "__end__" for the end of the list. Omit to
185
+ * leave an existing item in place / append a new item. Mirrors the
186
+ * section-level move_before_content_id.
187
+ */
188
+ move_before_item_id?: string;
179
189
  image_url?: string;
180
190
  caption?: string;
181
191
  }
@@ -185,10 +195,10 @@ export interface ImageGallerySectionArguments {
185
195
  gallery_type?: 'grid' | 'carousel' | 'masonry';
186
196
  is_full_width?: boolean;
187
197
  _replace_items?: boolean;
188
- /** 0-based indices of items to remove */
189
- _remove_item_indices?: number[];
190
- /** New order as array of current 0-based indices */
191
- _reorder_items?: number[];
198
+ /** Stable ids of items to remove. Order-independent (no index shifting). */
199
+ _remove_item_ids?: string[];
200
+ padding_top?: number;
201
+ padding_bottom?: number;
192
202
  }
193
203
 
194
204
  export interface GeneralLinkSectionArguments {
@@ -256,10 +266,15 @@ export interface TiktokSectionArguments {
256
266
  }
257
267
 
258
268
  export interface SocialLink {
259
- /** 0-based index of existing item to update in-place */
260
- item_index?: number;
261
- /** 0-based position to insert a new item at */
262
- insert_at?: number;
269
+ /** Stable id of the existing item to update or move. Omit to ADD a new item. */
270
+ item_id?: string;
271
+ /**
272
+ * Id of an existing item to position this item before (insert for new items,
273
+ * move for existing ones). Use "__end__" for the end of the list. Omit to
274
+ * leave an existing item in place / append a new item. Mirrors the
275
+ * section-level move_before_content_id.
276
+ */
277
+ move_before_item_id?: string;
263
278
  platform?:
264
279
  | 'facebook'
265
280
  | 'x'
@@ -280,10 +295,10 @@ export interface SocialLinksSectionArguments {
280
295
  /** Style for the icon container background (used when icon_style is 'square' or 'circle'). */
281
296
  icon_background_style?: TextStyleArguments;
282
297
  _replace_items?: boolean;
283
- /** 0-based indices of items to remove */
284
- _remove_item_indices?: number[];
285
- /** New order as array of current 0-based indices */
286
- _reorder_items?: number[];
298
+ /** Stable ids of items to remove. Order-independent (no index shifting). */
299
+ _remove_item_ids?: string[];
300
+ padding_top?: number;
301
+ padding_bottom?: number;
287
302
  }
288
303
 
289
304
  export interface MailingListSectionArguments {
@@ -292,6 +307,8 @@ export interface MailingListSectionArguments {
292
307
  accepted_fields?: 'emailOnly' | 'nameAndEmail';
293
308
  title_style?: TextStyleArguments;
294
309
  button_text_style?: ButtonTextStyleArguments;
310
+ padding_top?: number;
311
+ padding_bottom?: number;
295
312
  }
296
313
 
297
314
  export interface ContactFormFieldToggle {
@@ -324,6 +341,8 @@ export interface ContactFormSectionArguments {
324
341
  fields?: ContactFormFieldToggle[];
325
342
  background_color?: string | null;
326
343
  card_style?: CardStyleArguments;
344
+ padding_top?: number;
345
+ padding_bottom?: number;
327
346
  }
328
347
 
329
348
  export interface BannerSectionArguments {
@@ -350,6 +369,8 @@ export interface BannerSectionArguments {
350
369
  button_text_style?: ButtonTextStyleArguments;
351
370
  background_color?: string | null;
352
371
  card_style?: CardStyleArguments;
372
+ padding_top?: number;
373
+ padding_bottom?: number;
353
374
  }
354
375
 
355
376
  export interface AboutSectionArguments {
@@ -368,13 +389,20 @@ export interface AboutSectionArguments {
368
389
  title_style?: TextStyleArguments;
369
390
  paragraph_style?: TextStyleArguments;
370
391
  background_color?: string | null;
392
+ padding_top?: number;
393
+ padding_bottom?: number;
371
394
  }
372
395
 
373
396
  export interface ImagesSectionItem {
374
- /** 0-based index of existing item to update in-place */
375
- item_index?: number;
376
- /** 0-based position to insert a new item at */
377
- insert_at?: number;
397
+ /** Stable id of the existing item to update or move. Omit to ADD a new item. */
398
+ item_id?: string;
399
+ /**
400
+ * Id of an existing item to position this item before (insert for new items,
401
+ * move for existing ones). Use "__end__" for the end of the list. Omit to
402
+ * leave an existing item in place / append a new item. Mirrors the
403
+ * section-level move_before_content_id.
404
+ */
405
+ move_before_item_id?: string;
378
406
  image_url?: string;
379
407
  heading?: string;
380
408
  text?: string;
@@ -393,10 +421,8 @@ export interface ImagesSectionArguments {
393
421
  | 'center-card-aligned-image'
394
422
  | 'alternate-image';
395
423
  _replace_items?: boolean;
396
- /** 0-based indices of items to remove */
397
- _remove_item_indices?: number[];
398
- /** New order as array of current 0-based indices */
399
- _reorder_items?: number[];
424
+ /** Stable ids of items to remove. Order-independent (no index shifting). */
425
+ _remove_item_ids?: string[];
400
426
  title_style?: TextStyleArguments;
401
427
  description_style?: TextStyleArguments;
402
428
  heading_style?: TextStyleArguments;
@@ -409,10 +435,15 @@ export interface ImagesSectionArguments {
409
435
  }
410
436
 
411
437
  export interface TestimonialItem {
412
- /** 0-based index of existing item to update in-place */
413
- item_index?: number;
414
- /** 0-based position to insert a new item at */
415
- insert_at?: number;
438
+ /** Stable id of the existing item to update or move. Omit to ADD a new item. */
439
+ item_id?: string;
440
+ /**
441
+ * Id of an existing item to position this item before (insert for new items,
442
+ * move for existing ones). Use "__end__" for the end of the list. Omit to
443
+ * leave an existing item in place / append a new item. Mirrors the
444
+ * section-level move_before_content_id.
445
+ */
446
+ move_before_item_id?: string;
416
447
  testimonial?: string;
417
448
  name?: string;
418
449
  position?: string;
@@ -426,23 +457,28 @@ export interface TestimonialSectionArguments {
426
457
  items?: TestimonialItem[];
427
458
  display_style?: 'centered' | 'big-image' | 'columns';
428
459
  _replace_items?: boolean;
429
- /** 0-based indices of items to remove */
430
- _remove_item_indices?: number[];
431
- /** New order as array of current 0-based indices */
432
- _reorder_items?: number[];
460
+ /** Stable ids of items to remove. Order-independent (no index shifting). */
461
+ _remove_item_ids?: string[];
433
462
  title_style?: TextStyleArguments;
434
463
  testimonial_style?: TextStyleArguments;
435
464
  name_style?: TextStyleArguments;
436
465
  position_style?: TextStyleArguments;
437
466
  background_style?: BackgroundStyleArguments;
438
467
  card_style?: CardStyleArguments;
468
+ padding_top?: number;
469
+ padding_bottom?: number;
439
470
  }
440
471
 
441
472
  export interface ShapesSectionItem {
442
- /** 0-based index of existing item to update in-place */
443
- item_index?: number;
444
- /** 0-based position to insert a new item at */
445
- insert_at?: number;
473
+ /** Stable id of the existing item to update or move. Omit to ADD a new item. */
474
+ item_id?: string;
475
+ /**
476
+ * Id of an existing item to position this item before (insert for new items,
477
+ * move for existing ones). Use "__end__" for the end of the list. Omit to
478
+ * leave an existing item in place / append a new item. Mirrors the
479
+ * section-level move_before_content_id.
480
+ */
481
+ move_before_item_id?: string;
446
482
  icon_image_url?: string;
447
483
  shape_url?: string;
448
484
  color_theme?: string;
@@ -462,10 +498,8 @@ export interface ShapesSectionArguments {
462
498
  | 'center-card-aligned-image'
463
499
  | 'alternate-image';
464
500
  _replace_items?: boolean;
465
- /** 0-based indices of items to remove */
466
- _remove_item_indices?: number[];
467
- /** New order as array of current 0-based indices */
468
- _reorder_items?: number[];
501
+ /** Stable ids of items to remove. Order-independent (no index shifting). */
502
+ _remove_item_ids?: string[];
469
503
  title_style?: TextStyleArguments;
470
504
  description_style?: TextStyleArguments;
471
505
  heading_style?: TextStyleArguments;
@@ -485,6 +519,8 @@ export interface BookingSectionArguments {
485
519
  description_style?: TextStyleArguments;
486
520
  button_text_style?: ButtonTextStyleArguments;
487
521
  background_color?: string | null;
522
+ padding_top?: number;
523
+ padding_bottom?: number;
488
524
  }
489
525
 
490
526
  export interface DividerSectionArguments {
@@ -11,6 +11,7 @@ import type {
11
11
  UpdateItemValuePayload,
12
12
  } from '../../models';
13
13
  import cloneDeep from 'lodash.clonedeep';
14
+ import * as uuid from 'uuid';
14
15
 
15
16
  const ACCEPTED_MIME_TYPES = 'image/png, image/jpeg, image/svg+xml';
16
17
 
@@ -88,6 +89,7 @@ export default defineComponent({
88
89
  },
89
90
  addNewItem(): void {
90
91
  const newItem: LinksItem = {
92
+ id: uuid.v4(),
91
93
  height: 0,
92
94
  imageTitle: '',
93
95
  width: 0,
package/src/tracker.ts ADDED
@@ -0,0 +1,28 @@
1
+ // Lightweight subpath entry: `@designcrowd/library.brand-page/tracker`.
2
+ // Re-exports ONLY the analytics surface (useEventTracker + event constants),
3
+ // which depends solely on `vue`. This lets consumers import the tracker without
4
+ // pulling the full library graph (maker, TipTap, renderer). See
5
+ // docs/plans/multi-entrypoint-tree-shaking.md and the /tracker proposal.
6
+ //
7
+ // IMPORTANT: useEventTracker holds module-scoped pub/sub state (LISTENERS,
8
+ // GLOBAL_EVENT_PROPS). It MUST resolve to a single shared instance across this
9
+ // entry and the root `.` entry, or subscribers registered here won't receive
10
+ // events published via the root. The multi-entry build hoists it into a shared
11
+ // chunk (both `index` and `tracker` import it) — verified at build time.
12
+ export {
13
+ useEventTracker,
14
+ EVENTS,
15
+ SELECT_LOCATIONS,
16
+ UPGRADE_CTA_LOCATIONS,
17
+ AI_CTA_LOCATIONS,
18
+ TRIGGER_TYPE,
19
+ PUBLISH_MODAL_CONTINUATION_EVENTS,
20
+ GENERATE_TEXT_CONTINUATION_EVENTS,
21
+ PUBLISH_OPTION_MODAL_CONTINUATION_EVENTS,
22
+ } from './brand-page-maker/composables/useEventTracker';
23
+ export type {
24
+ EventTracker,
25
+ BpEventTrackerEvent,
26
+ BpGlobalEventProps,
27
+ BpUpsellEventProps,
28
+ } from './brand-page-maker/composables/useEventTracker';
@@ -19,7 +19,12 @@ import {
19
19
  resolveLogoData,
20
20
  } from './build-asset-manifest';
21
21
 
22
- type MinifiedFooterSocialLink = { imageUrl: string; url: string };
22
+ type MinifiedFooterSocialLink = {
23
+ position: number;
24
+ id?: string;
25
+ imageUrl: string;
26
+ url: string;
27
+ };
23
28
 
24
29
  type MinifiedFooter = {
25
30
  displayStyle?: string;
@@ -284,6 +289,7 @@ const minifyContent = (
284
289
  ...(Array.isArray(c.items) && {
285
290
  items: c.items.map((item: any, index: number) => ({
286
291
  position: index + 1,
292
+ ...(item.id && { id: item.id }),
287
293
  ...st('name', item.name, editor),
288
294
  ...st('jobTitle', item.position, editor),
289
295
  ...st('testimonials', item.testimonials, editor),
@@ -312,6 +318,7 @@ const minifyContent = (
312
318
  ...(Array.isArray(c.items) && {
313
319
  items: c.items.map((item: any, index: number) => ({
314
320
  position: index + 1,
321
+ ...(item.id && { id: item.id }),
315
322
  ...includeImageWithKey(item.imageUrl),
316
323
  ...st('title', item.title, editor),
317
324
  ...st('paragraph', item.paragraph, editor),
@@ -342,6 +349,7 @@ const minifyContent = (
342
349
  ...(Array.isArray(c.textItems) && {
343
350
  textItems: c.textItems.map((item: any, index: number) => ({
344
351
  position: index + 1,
352
+ ...(item.id && { id: item.id }),
345
353
  ...st('text', item.text, editor),
346
354
  ...(includeStyle(item.textStyle) && { textStyle: item.textStyle }),
347
355
  })),
@@ -431,6 +439,7 @@ const minifyContent = (
431
439
  ...(Array.isArray(c.links) && {
432
440
  links: c.links.map((link: any, index: number) => ({
433
441
  position: index + 1,
442
+ ...(link.id && { id: link.id }),
434
443
  ...(includeString(link.url) && { url: link.url }),
435
444
  ...includeImageWithKey(link.imageUrl),
436
445
  })),
@@ -474,6 +483,7 @@ const minifyContent = (
474
483
  ...(Array.isArray(c.images) && {
475
484
  images: c.images.map((img: any, index: number) => ({
476
485
  position: index + 1,
486
+ ...(img.id && { id: img.id }),
477
487
  ...includeImageWithKey(img.imageUrl),
478
488
  ...st('imageTitle', img.imageTitle, editor),
479
489
  })),
@@ -523,7 +533,9 @@ const minifyFooter = (
523
533
  logoImageWidth: footer.logoImageWidth || undefined,
524
534
  ...(logoAssetKey && { logoAssetKey }),
525
535
  socialLinks: footer.socialLinks?.length
526
- ? footer.socialLinks.map((l) => ({
536
+ ? footer.socialLinks.map((l, index) => ({
537
+ position: index + 1,
538
+ ...(l.id && { id: l.id }),
527
539
  imageUrl: l.imageUrl || '',
528
540
  url: l.url || '',
529
541
  }))
@@ -1,13 +0,0 @@
1
- import type { BrandPageModel } from '../../brand-page/models';
2
- import { BaseMetadataBuilder, type BuilderResult, type MetadataBuilderRequest } from '../models';
3
- import type { Logger } from '../logger';
4
- export interface ReorderPagesPayload {
5
- pageOrder: string[];
6
- }
7
- export declare class ReorderPagesBuilder extends BaseMetadataBuilder<ReorderPagesPayload> {
8
- constructor(request: MetadataBuilderRequest<ReorderPagesPayload>, logger?: Logger);
9
- validateAsync(): Promise<{
10
- errors: string[];
11
- }>;
12
- buildAsync(model: BrandPageModel): Promise<BuilderResult>;
13
- }
@@ -1,49 +0,0 @@
1
- import { BaseMetadataBuilder, } from '../models';
2
- export class ReorderPagesBuilder extends BaseMetadataBuilder {
3
- constructor(request, logger) {
4
- super(request, logger);
5
- }
6
- async validateAsync() {
7
- const errors = [];
8
- const payload = this.request.payload;
9
- if (!payload?.pageOrder?.length) {
10
- errors.push('page_order is required for reorder_pages');
11
- }
12
- return { errors };
13
- }
14
- async buildAsync(model) {
15
- this.log('Build called');
16
- const validation = await this.validateAsync();
17
- if (validation.errors.length) {
18
- return { request: this.request, model, errors: validation.errors };
19
- }
20
- const { pageOrder } = this.request.payload;
21
- // Build pageId → routeId lookup from nav.routes
22
- const pageIdToRouteId = new Map();
23
- for (const route of model.nav.routes) {
24
- pageIdToRouteId.set(route.pageId, route.id);
25
- }
26
- // Build new nav.orders from the flat page_order (array of pageIds)
27
- const newOrders = [];
28
- const seenRouteIds = new Set();
29
- for (const pageId of pageOrder) {
30
- const routeId = pageIdToRouteId.get(pageId);
31
- if (!routeId)
32
- continue;
33
- newOrders.push({ routeId, subRoutes: [] });
34
- seenRouteIds.add(routeId);
35
- }
36
- // Safety: append any routes not mentioned (don't lose pages)
37
- for (const route of model.nav.routes) {
38
- const routeId = pageIdToRouteId.get(route.pageId);
39
- if (routeId && !seenRouteIds.has(routeId)) {
40
- newOrders.push({ routeId, subRoutes: [] });
41
- }
42
- }
43
- model.nav.orders = newOrders;
44
- this.log({
45
- message: `Reordered pages: ${pageOrder.join(', ')}`,
46
- });
47
- return { request: this.request, model };
48
- }
49
- }