@designcrowd/library.brand-page 5.17.25-undo-21 → 5.17.25

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 (115) hide show
  1. package/lib/src/{CreatedOnBrandCrowd-de-de-Bze5WUip.js → CreatedOnBrandCrowd-de-de-9YvB5ocm.js} +1 -1
  2. package/lib/src/{CreatedOnBrandCrowd-en-us-Dw5uSnX8.js → CreatedOnBrandCrowd-en-us-B8OpZpMP.js} +1 -1
  3. package/lib/src/{CreatedOnBrandCrowd-es-es-Cz-oPdjt.js → CreatedOnBrandCrowd-es-es-D_d6SwvE.js} +1 -1
  4. package/lib/src/{CreatedOnBrandCrowd-fr-fr-zldLXw3G.js → CreatedOnBrandCrowd-fr-ca-CPqNeevd.js} +1 -1
  5. package/lib/src/{CreatedOnBrandCrowd-fr-ca-zldLXw3G.js → CreatedOnBrandCrowd-fr-fr-CPqNeevd.js} +1 -1
  6. package/lib/src/{CreatedOnBrandCrowd-pt-pt-64QJpEaD.js → CreatedOnBrandCrowd-pt-br-D0DpxtjY.js} +1 -1
  7. package/lib/src/{CreatedOnBrandCrowd-pt-br-64QJpEaD.js → CreatedOnBrandCrowd-pt-pt-D0DpxtjY.js} +1 -1
  8. package/lib/src/{CreatedOnDcom-de-de-8SXOvjEH.js → CreatedOnDcom-de-de-BLKiD1pl.js} +1 -1
  9. package/lib/src/{CreatedOnDcom-en-us-ClZBYY_t.js → CreatedOnDcom-en-us-DGNjFV5B.js} +1 -1
  10. package/lib/src/{CreatedOnDcom-es-es-xg4ZwvEb.js → CreatedOnDcom-es-es-C2IDMZZe.js} +1 -1
  11. package/lib/src/{CreatedOnDcom-fr-ca-4zo2gxc1.js → CreatedOnDcom-fr-ca-ZRxz3QKj.js} +1 -1
  12. package/lib/src/{CreatedOnDcom-fr-fr-Dw9Fuy7U.js → CreatedOnDcom-fr-fr-5bE7oSdF.js} +1 -1
  13. package/lib/src/{CreatedOnDcom-pt-br-Dbl0EWcU.js → CreatedOnDcom-pt-br-WCumEx63.js} +1 -1
  14. package/lib/src/{CreatedOnDcom-pt-pt-Bps73oQE.js → CreatedOnDcom-pt-pt-C8Hinnmg.js} +1 -1
  15. package/lib/src/brand-page-maker/components/action-bar/ActionBar.mixin.d.ts +2 -7
  16. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.d.ts +2 -7
  17. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.js +0 -10
  18. package/lib/src/brand-page-maker/components/brand-page-maker/BrandPageMakerTemplateSettings.mixin.d.ts +2 -7
  19. package/lib/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.js +0 -1
  20. package/lib/src/brand-page-maker/components/brandpage-maker-page/BrandPageMakerPage.mixin.d.ts +2 -23
  21. package/lib/src/brand-page-maker/components/brandpage-settings/ReplaceImageSettingsForBrandPageSettings.mixin.d.ts +2 -7
  22. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.d.ts +0 -3
  23. package/lib/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.js +0 -11
  24. package/lib/src/brand-page-maker/components/seo-settings/SeoPageSettings.mixin.d.ts +2 -7
  25. package/lib/src/brand-page-maker/components/seo-settings/UrlSettings.mixin.d.ts +2 -7
  26. package/lib/src/brand-page-maker/components/social-share/SocialShare.mixin.d.ts +2 -7
  27. package/lib/src/brand-page-maker/components/template-preview/TemplatePreview.mixin.d.ts +2 -7
  28. package/lib/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.js +0 -9
  29. package/lib/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.js +1 -17
  30. package/lib/src/brand-page-maker/composables/useEventTracker.d.ts +0 -20
  31. package/lib/src/brand-page-maker/composables/useEventTracker.js +0 -17
  32. package/lib/src/brand-page-maker/stores/ai-thread/ai-website-builder-thread-store.d.ts +1 -1
  33. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.d.ts +2 -41
  34. package/lib/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.js +5 -247
  35. package/lib/src/brand-page-maker/stores/logo-store/logo-store.d.ts +0 -1
  36. package/lib/src/brand-page-maker/stores/user/user-store.js +0 -1
  37. package/lib/src/bundled-translations-Bu_5FO26.js +173 -0
  38. package/lib/src/bundles/bundled-translations.json +1 -3
  39. package/lib/src/constants/ui.d.ts +0 -1
  40. package/lib/src/css/library.brand-page-brandCrowd.min.css +17 -22
  41. package/lib/src/css/library.brand-page-brandPage.min.css +18 -22
  42. package/lib/src/css/library.brand-page-designCom.min.css +17 -22
  43. package/lib/src/css/library.brand-page-preview.min.css +17 -22
  44. package/lib/src/index.mjs +1 -1
  45. package/lib/src/{lib-BgvZxeSl.js → lib-BF9ONIgx.js} +23292 -23913
  46. package/lib/src/shared/content-settings/components/GenericTextInput.mixin.d.ts +1 -1
  47. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.mixin.d.ts +2 -23
  48. package/lib/src/shared/content-settings/components/content-settings/ContentSettings.stories.js +0 -1
  49. package/lib/src/shared/content-settings/components/content-settings/ReplaceImageSettingsForContent.mixin.d.ts +2 -23
  50. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItem.mixin.d.ts +0 -16
  51. package/lib/src/shared/content-settings/components/content-settings-item/ContentSettingsItemField.mixin.d.ts +2 -7
  52. package/lib/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.d.ts +0 -3
  53. package/lib/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.js +2 -19
  54. package/lib/src/shared/content-settings/components/email-field/EmailField.mixin.d.ts +1 -1
  55. package/lib/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.js +8 -18
  56. package/lib/src/shared/content-settings/components/radio-field/RadioField.mixin.d.ts +1 -1
  57. package/lib/src/shared/content-settings/components/tel-field/TelField.mixin.d.ts +1 -1
  58. package/lib/src/shared/content-settings/components/text-field/TextField.mixin.d.ts +1 -1
  59. package/lib/src/shared/content-settings/components/textarea-field/TextareaField.mixin.d.ts +1 -1
  60. package/lib/src/shared/content-settings/components/url-field/UrlField.mixin.d.ts +1 -1
  61. package/lib/src/shared/content-settings/components/youtube-video-field/YoutubeVideoField.mixin.d.ts +1 -1
  62. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.d.ts +0 -18
  63. package/lib/src/shared/logo-settings/components/EditLogoSettings.mixin.js +0 -11
  64. package/lib/src/shared/logo-settings/components/LogoSettings.mixin.d.ts +2 -9
  65. package/lib/src/shared/logo-settings/store/editable-logo-store.d.ts +1 -9
  66. package/lib/src/shared/logo-settings/store/editable-logo-store.js +0 -33
  67. package/lib/src/shared/page-settings/components/page-settings/PageSettings.mixin.d.ts +2 -24
  68. package/lib/src/shared/page-settings/components/page-settings-section-styles/PageSettingsSectionStyles.mixin.d.ts +2 -9
  69. package/lib/src/shared/publish-brand-page-with-state/PublishBrandPageModalWithState.mixin.d.ts +2 -23
  70. package/lib/src/shared/side-bar/components/SideBarTabButton.mixin.d.ts +0 -16
  71. package/lib/src/shared/style-settings/components/StyleSettings.mixin.d.ts +2 -23
  72. package/lib/src/shared/template-settings/components/TemplatesSettings.mixin.d.ts +0 -16
  73. package/package.json +1 -1
  74. package/src/brand-page/components/video/Video.vue +0 -2
  75. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.mixin.ts +0 -11
  76. package/src/brand-page-maker/components/brand-page-maker/BrandPageMaker.vue +3 -2
  77. package/src/brand-page-maker/components/brand-page-maker/__fixtures__/brand-page-examples.ts +0 -1
  78. package/src/brand-page-maker/components/cookie-collection/CookieSettings.vue +17 -18
  79. package/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.mixin.ts +0 -13
  80. package/src/brand-page-maker/components/quick-link-bar/QuickLinkBar.vue +4 -14
  81. package/src/brand-page-maker/components/quick-link-bar/i18n/quick-link-bar.json +1 -3
  82. package/src/brand-page-maker/components/save-contact/SaveContact.vue +19 -12
  83. package/src/brand-page-maker/composables/ai-chat/useAiWebsiteBuilderChat.ts +0 -10
  84. package/src/brand-page-maker/composables/rich-text-editor/useRichTextEditor.ts +1 -20
  85. package/src/brand-page-maker/composables/useEventTracker.ts +0 -29
  86. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.test.ts +0 -166
  87. package/src/brand-page-maker/stores/brandpage-maker/brandpage-maker-store.ts +4 -287
  88. package/src/brand-page-maker/stores/logo-store/logo-store.ts +0 -5
  89. package/src/brand-page-maker/stores/user/user-store.ts +0 -1
  90. package/src/bundles/bundled-translations.json +1 -3
  91. package/src/constants/ui.ts +0 -1
  92. package/src/shared/content-settings/components/content-settings/ContentSettings.stories.ts +0 -1
  93. package/src/shared/content-settings/components/draggable-item-list/DraggableItemList.mixin.ts +2 -19
  94. package/src/shared/content-settings/components/draggable-item-list/DraggableItemList.vue +1 -1
  95. package/src/shared/content-settings/components/form-fields-field/FormFieldsField.mixin.ts +10 -26
  96. package/src/shared/content-settings/components/logo-selection-field/LogoSelection.vue +48 -63
  97. package/src/shared/content-settings/components/text-field/TextField.vue +0 -1
  98. package/src/shared/content-settings/components/text-image-field/TextImageField.vue +4 -33
  99. package/src/shared/logo-settings/components/EditLogoSettings.mixin.ts +0 -11
  100. package/src/shared/logo-settings/store/editable-logo-store.ts +1 -45
  101. package/lib/src/brand-page-maker/composables/useUndoRedoFlag.d.ts +0 -16
  102. package/lib/src/brand-page-maker/composables/useUndoRedoFlag.js +0 -22
  103. package/lib/src/brand-page-maker/stores/history/history-store.d.ts +0 -104
  104. package/lib/src/brand-page-maker/stores/history/history-store.js +0 -836
  105. package/lib/src/bundled-translations-CWHKs3in.js +0 -173
  106. package/lib/src/metadata-builder/builders/reorder-pages-builder.d.ts +0 -13
  107. package/lib/src/metadata-builder/builders/reorder-pages-builder.js +0 -49
  108. package/src/brand-page-maker/components/quick-link-bar/RedoButton.test.ts +0 -166
  109. package/src/brand-page-maker/components/quick-link-bar/RedoButton.vue +0 -66
  110. package/src/brand-page-maker/components/quick-link-bar/UndoButton.test.ts +0 -166
  111. package/src/brand-page-maker/components/quick-link-bar/UndoButton.vue +0 -66
  112. package/src/brand-page-maker/composables/useUndoRedoFlag.ts +0 -26
  113. package/src/brand-page-maker/stores/history/history-store.test.ts +0 -1246
  114. package/src/brand-page-maker/stores/history/history-store.ts +0 -1041
  115. package/src/metadata-builder/builders/reorder-pages-builder.ts +0 -80
@@ -40,13 +40,6 @@ import { useUiBackgroundSettingsStore } from '@shared/background-settings/store'
40
40
  import { useBrandPageMakerUserStore } from '../user';
41
41
  import { useBrandPageContainerStore } from '../brandpage-container';
42
42
  import { useUiTemplateSettingsStore } from '@shared/template-settings/store';
43
- import { useUiNavigationStyleSettingsStore } from '@shared/navigation-style-settings/store';
44
- import {
45
- CONTENT_SETTINGS_MODES,
46
- useUiContentSettingsStore,
47
- } from '@shared/content-settings/store';
48
- import { useUiSideBarStore } from '@shared/side-bar/store';
49
- import { SIDE_BAR_TAB_NAMES } from '@brand-page-maker/constants';
50
43
  import { PAGE_SECTIONS } from '@shared/page-settings/models';
51
44
  import { FabricRenderClient } from '../../../clients/fabric-render-client';
52
45
  import { setLocaleAsync } from '@src/composables/useTranslate';
@@ -567,163 +560,4 @@ describe('brand page maker store module', () => {
567
560
  // Assert
568
561
  expect(setDesignTemplateTokenSpy).not.toHaveBeenCalled();
569
562
  });
570
-
571
- // BP-5506 regression: applyHistorySnapshot must replace nested navigation
572
- // style refs as a whole, not deep-merge them. The previous bug was that
573
- // object-form $patch would leave keys present in the live state but absent
574
- // from the snapshot in place — e.g. switching nav button styles then
575
- // undoing would leave the new style's borderColor / borderRadius applied.
576
- test('applyHistorySnapshot replaces navigation styles wholesale (no key bleed across undo)', async () => {
577
- // Arrange — initialise the maker so `applyHistorySnapshot` has a
578
- // populated set of dependent stores to write into.
579
- const makerStore = useBrandPageMakerStore();
580
- const navigationStyleSettingsStore = useUiNavigationStyleSettingsStore();
581
-
582
- await makerStore.initialise(INITIALISE_PAYLOAD);
583
-
584
- // Build snapshot A — the "older" state we'll undo back to. Keep the
585
- // navigation simple: only borderRadius is set on selectedButtonStyle.
586
- const snapshotA = {
587
- ...makerStore.brandPageData,
588
- pageHeaderDesktop: {
589
- ...makerStore.brandPageData.pageHeaderDesktop,
590
- navigation: {
591
- ...makerStore.brandPageData.pageHeaderDesktop?.navigation,
592
- selectedButtonStyle: {
593
- borderRadius: '0px',
594
- },
595
- },
596
- },
597
- } as BrandPageModel;
598
-
599
- // Build snapshot B — the "newer" state with extra keys that the
600
- // older snapshot does NOT have. If $patch deep-merged, these would
601
- // survive an undo back to A.
602
- const snapshotB = {
603
- ...snapshotA,
604
- pageHeaderDesktop: {
605
- ...snapshotA.pageHeaderDesktop,
606
- navigation: {
607
- ...snapshotA.pageHeaderDesktop?.navigation,
608
- selectedButtonStyle: {
609
- borderRadius: '12px',
610
- borderColor: 'rgb(255, 0, 0)',
611
- borderStyle: 'dashed',
612
- borderWidth: '4px',
613
- },
614
- },
615
- },
616
- } as BrandPageModel;
617
-
618
- // Act — apply newer first (simulates the user making the edit), then
619
- // older (simulates the undo). NavigationDesktop's constructor folds
620
- // its input over a defaults object, so we read the resulting state
621
- // and assert against the keys that came from snapshotB but NOT from
622
- // snapshotA.
623
- await makerStore.applyHistorySnapshot(snapshotB);
624
- await makerStore.applyHistorySnapshot(snapshotA);
625
-
626
- // Assert — after the undo, no keys from B should bleed through onto
627
- // A's selectedButtonStyle. Specifically the values that are unique to
628
- // B (red dashed border) must not be present.
629
- const restored =
630
- navigationStyleSettingsStore.navigation[PAGE_SECTIONS.header][
631
- VIEWPORTS.desktop
632
- ];
633
- const restoredSelected = restored.selectedButtonStyle;
634
-
635
- expect(restoredSelected.borderRadius).toBe('0px');
636
- // The B-only values must not survive the undo. NavigationStyles'
637
- // constructor seeds defaults for selectedButtonStyle, so we assert
638
- // the *exact* B-only values are gone rather than a strict superset.
639
- expect(restoredSelected.borderColor).not.toBe('rgb(255, 0, 0)');
640
- expect(restoredSelected.borderStyle).not.toBe('dashed');
641
- expect(restoredSelected.borderWidth).not.toBe('4px');
642
- });
643
-
644
- // BP-5506 regression: undoing a page-add must clear the URL/active-page
645
- // back to a page that exists in the restored snapshot. The page-tabs
646
- // carousel keys "selected" off the route URL; after `setNav` removes the
647
- // added route, no nav item matches the URL slug and the highlight
648
- // disappears. `applyHistorySnapshot` accepts an `activePageId` and writes
649
- // it through `pageNavigationStore.setCurrentActivePage` so the routeStore
650
- // and `currentPage` getter realign.
651
- test('applyHistorySnapshot updates pageNavigation.activePageId when given a valid id', async () => {
652
- const makerStore = useBrandPageMakerStore();
653
- const pageNavigationStore = usePageNavigationStore();
654
-
655
- await makerStore.initialise(INITIALISE_PAYLOAD);
656
-
657
- const restoredPageId = pageNavigationStore.routes[0]?.pageId;
658
-
659
- expect(restoredPageId).toBeDefined();
660
-
661
- pageNavigationStore.activePageId = '';
662
-
663
- await makerStore.applyHistorySnapshot(
664
- makerStore.brandPageData,
665
- restoredPageId,
666
- );
667
-
668
- expect(pageNavigationStore.activePageId).toBe(restoredPageId);
669
- });
670
-
671
- test('applyHistorySnapshot falls back to first/default route when no activePageId is provided', async () => {
672
- const makerStore = useBrandPageMakerStore();
673
- const pageNavigationStore = usePageNavigationStore();
674
-
675
- await makerStore.initialise(INITIALISE_PAYLOAD);
676
-
677
- const expectedFirstId =
678
- pageNavigationStore.getFirstOrDefaultRoute()?.pageId;
679
-
680
- expect(expectedFirstId).toBeDefined();
681
-
682
- pageNavigationStore.activePageId = 'stale-page-id-not-in-nav';
683
-
684
- await makerStore.applyHistorySnapshot(makerStore.brandPageData);
685
-
686
- expect(pageNavigationStore.activePageId).toBe(expectedFirstId);
687
- });
688
-
689
- // BP-5506 regression: when undoing a content add, the selected item
690
- // disappears. The fallback must restore the Content tab's natural
691
- // landing state (mode = edit, selectedItemId = null), not just switch
692
- // tabs — otherwise `<FocusedContentItemSettings>` keeps rendering for
693
- // a missing item and paints a blank panel.
694
- test('applyHistorySnapshot resets sidebar to default Content tab when selected item is gone', async () => {
695
- const makerStore = useBrandPageMakerStore();
696
- const contentSettingsStore = useUiContentSettingsStore();
697
- const sideBarStore = useUiSideBarStore();
698
-
699
- await makerStore.initialise(INITIALISE_PAYLOAD);
700
-
701
- // Simulate the user opening a content item's focused settings.
702
- const orphanItemId = 'orphan-content-id-not-in-snapshot';
703
-
704
- contentSettingsStore.pushMode({
705
- mode: CONTENT_SETTINGS_MODES.focusedContentItem,
706
- selectedItemId: orphanItemId,
707
- });
708
- contentSettingsStore.selectedItemId = orphanItemId;
709
- sideBarStore.setSelectedTabName({
710
- tabName: SIDE_BAR_TAB_NAMES.CONTENT_SETTINGS,
711
- });
712
-
713
- // Pre-condition: panel is showing the focused content item.
714
- expect(contentSettingsStore.mode).toBe(
715
- CONTENT_SETTINGS_MODES.focusedContentItem,
716
- );
717
- expect(contentSettingsStore.selectedItemId).toBe(orphanItemId);
718
-
719
- // Snapshot does NOT contain orphanItemId — simulates undo of an add.
720
- await makerStore.applyHistorySnapshot(makerStore.brandPageData);
721
-
722
- // Post-condition: same shape as a fresh Content tab click.
723
- expect(contentSettingsStore.mode).toBe(CONTENT_SETTINGS_MODES.edit);
724
- expect(contentSettingsStore.selectedItemId).toBeNull();
725
- expect(sideBarStore.selectedTabName).toBe(
726
- SIDE_BAR_TAB_NAMES.CONTENT_SETTINGS,
727
- );
728
- });
729
563
  });
@@ -59,7 +59,6 @@ import {
59
59
  BRANDPAGE_DEFAULT_SAVE_MESSAGE_DESCRIPTION,
60
60
  MAKER_MOBILE_BREAKPOINT,
61
61
  MESSAGE_VISIBLE_DURATION,
62
- SIDE_BAR_TAB_NAMES,
63
62
  } from '@brand-page-maker/constants';
64
63
  import { useUiSideBarStore } from '@shared/side-bar/store';
65
64
  import { delayInMS } from '../../../utils/ui-util';
@@ -101,7 +100,6 @@ import { useConsent } from '@brand-page/composables/consent.composable';
101
100
  import { commonTr, errorTr } from '@src/composables/useTranslate';
102
101
  import { UPGRADE_CTA_LOCATIONS } from '../../composables/useEventTracker';
103
102
  import { useVideoStore } from '../video';
104
- import { useHistoryStore } from '../history/history-store';
105
103
  import { findValuesByKey } from '../../../helpers/ts.helper';
106
104
  import { useLogoStore } from '../logo-store';
107
105
 
@@ -666,16 +664,6 @@ export const useBrandPageMakerStore = defineStore('brandPageMaker', {
666
664
  description: payload.noDescription ? '' : description,
667
665
  },
668
666
  });
669
-
670
- // Any successful save re-baselines the undo stack so a user can't
671
- // undo back past the persisted state. This covers Save Progress /
672
- // Publish AND settings-only saves (updateBrandPage,
673
- // updateSensitiveContent, admin saveTemplate) — settings-tab
674
- // actions persist the metadata, so allowing undo to roll them
675
- // back would let the local stack drift from what's saved.
676
- // reset() no-ops in non-undo-redo sessions (no baseline = nothing
677
- // to clear).
678
- useHistoryStore().reset();
679
667
  },
680
668
  hideChangeTemplateConfirmModal() {
681
669
  this.setIsChangeTemplateConfirmModalVisible({
@@ -689,11 +677,10 @@ export const useBrandPageMakerStore = defineStore('brandPageMaker', {
689
677
  this.setSelectedTemplateId({ templateId: false });
690
678
  this.hideChangeTemplateConfirmModal();
691
679
  },
692
- async onChangeTemplateConfirmModalConfirm() {
693
- await this.applySelectedTemplate();
680
+ onChangeTemplateConfirmModalConfirm() {
681
+ this.applySelectedTemplate();
694
682
  this.hideChangeTemplateConfirmModal();
695
683
  this.isModifiedAfterTemplateApplied = false;
696
- this._captureTemplateSwitchIfEnabled();
697
684
  },
698
685
  onContentChanged() {
699
686
  this.setIsModifiedAfterTemplateApplied();
@@ -701,7 +688,7 @@ export const useBrandPageMakerStore = defineStore('brandPageMaker', {
701
688
  onStyleChanged(): void {
702
689
  this.setIsModifiedAfterTemplateApplied();
703
690
  },
704
- async onTemplateSelected(payload: OnTemplateSelectedPayload) {
691
+ onTemplateSelected(payload: OnTemplateSelectedPayload) {
705
692
  this.setSelectedTemplateId({
706
693
  templateId: payload.templateId,
707
694
  });
@@ -711,8 +698,7 @@ export const useBrandPageMakerStore = defineStore('brandPageMaker', {
711
698
  isVisible: true,
712
699
  });
713
700
  } else {
714
- await this.applySelectedTemplate();
715
- this._captureTemplateSwitchIfEnabled();
701
+ this.applySelectedTemplate();
716
702
  }
717
703
  },
718
704
  setSelectedTabName(payload) {
@@ -749,275 +735,6 @@ export const useBrandPageMakerStore = defineStore('brandPageMaker', {
749
735
  const uiContentSettingsStore = useUiContentSettingsStore();
750
736
 
751
737
  uiContentSettingsStore.updateContent<TContent>(payload);
752
-
753
- // NOTE (Bolt 1, 2026-04-23): this action is not the actual edit path used
754
- // by the UI — sidebar inputs call uiContentSettingsStore.updateItemValue
755
- // directly. History capture is driven by a deep `watch(brandPageData, ...)`
756
- // installed in history-store once the baseline is captured, so we do not
757
- // need to instrument this action. Kept here as a pass-through because
758
- // removing it may affect other callers.
759
- },
760
- /**
761
- * Restore every store that the `brandPageData` getter reads from, so undo
762
- * and redo bring back the EXACT document state captured at snapshot time.
763
- *
764
- * `initialiseStoresByBrandPageModelAsync` is too narrow for undo/redo — it
765
- * only rehydrates pages, sections, and nav structure, which is enough for
766
- * the AI-apply path but leaves navigation styles, backgrounds, button/text/
767
- * logo/gallery styles, shop config, and several own-store fields unchanged.
768
- * This action mirrors the full hydration surface of `applySelectedTemplate`
769
- * (minus the template-specific logo/personalisation logic) so a restore is
770
- * a complete round-trip of what `brandPageData` returns.
771
- *
772
- * Called ONLY by history-store during undo/redo. The history store sets
773
- * `isApplyingSnapshot=true` across this call to prevent the deep-watcher
774
- * on `brandPageData` from re-capturing the restored state.
775
- */
776
- /**
777
- * Force-commit a `template_switch` entry to the history store, if the
778
- * history store has a baseline (which only happens when the undo/redo
779
- * feature flag is on — see `BrandPageMaker.mixin.ts`).
780
- */
781
- _captureTemplateSwitchIfEnabled(): void {
782
- const historyStore = useHistoryStore();
783
-
784
- if (!historyStore.baselineCaptured) return;
785
-
786
- historyStore.captureTemplateSwitch();
787
- },
788
- async applyHistorySnapshot(
789
- snapshot: BrandPageModel,
790
- activePageId?: string,
791
- ): Promise<void> {
792
- const styleSettingsStore = useUiStyleSettingsStore();
793
- const navigationStyleSettingsStore = useUiNavigationStyleSettingsStore();
794
- const backgroundSettingsStore = useUiBackgroundSettingsStore();
795
- const pageNavigationStore = usePageNavigationStore();
796
- const shopStore = useShopConfigStore();
797
-
798
- // 1. Pages + header/footer sections (also covers content items).
799
- this.initialisePagesForCurrentPage({
800
- pages: snapshot.pages,
801
- sections: {
802
- [PAGE_SECTIONS.header]: {
803
- [VIEWPORTS.desktop]: snapshot.pageHeaderDesktop,
804
- [VIEWPORTS.mobile]: snapshot.pageHeaderMobile,
805
- },
806
- [PAGE_SECTIONS.footer]: {
807
- [VIEWPORTS.desktop]: snapshot.pageFooterDesktop,
808
- [VIEWPORTS.mobile]: snapshot.pageFooterMobile,
809
- },
810
- },
811
- });
812
-
813
- // 2. Page-level nav structure (routes, orders).
814
- pageNavigationStore.setNav({ nav: snapshot.nav });
815
-
816
- // 3. Navigation STYLES (header/footer nav colors, alignment, etc.) —
817
- // this is the bit that was being dropped before.
818
- // Use function-form $patch so each nested style object is replaced as a
819
- // whole reference. The object-form $patch (used by setState) deep-merges
820
- // and would leave keys present in the live state but absent from the
821
- // snapshot in place — e.g. switching button styles then undoing would
822
- // leave the new style's borderRadius/borderStyle/borderWidth applied.
823
- navigationStyleSettingsStore.$patch((state) => {
824
- state.navigation[PAGE_SECTIONS.header][VIEWPORTS.desktop] =
825
- new NavigationDesktop(snapshot.pageHeaderDesktop?.navigation);
826
- state.navigation[PAGE_SECTIONS.header][VIEWPORTS.mobile] =
827
- new NavigationMobile(snapshot.pageHeaderMobile?.navigation);
828
- });
829
-
830
- // 4. Global styles (logo, button, text, gallery). Same whole-reference
831
- // replacement reasoning as step 3.
832
- styleSettingsStore.$patch((state) => {
833
- state.buttonStyles = snapshot.styles.buttonStyles;
834
- state.logoStyles = snapshot.styles.logoStyles;
835
- state.textStyles = snapshot.styles.textStyles;
836
- state.galleryStyles = snapshot.styles.galleryStyles;
837
- });
838
-
839
- // 5. Background styles. Same whole-reference replacement reasoning as
840
- // step 3 — `setBackgroundStyles` calls per-field setters that
841
- // `Object.assign` partials onto the live `style` and
842
- // `backgroundShapeStyle` objects, so keys from the previous mode
843
- // (e.g. background image url, shape style props) would survive an
844
- // undo back to a state that doesn't have them.
845
- backgroundSettingsStore.$patch((state) => {
846
- state.backgroundStyles = snapshot.styles.backgroundStyles;
847
- });
848
- backgroundSettingsStore.onBackgroundChanged();
849
-
850
- // 6. Shop products are derived from shop-content blocks inside `pages`,
851
- // so they're already implicitly restored at step 1. Re-run the shop
852
- // store's derivation to keep `products` in sync with the snapshot
853
- // pages. paymentConfig is intentionally NOT restored — selecting it is
854
- // backend-confirmed (Stripe Connect / payment-provider linkage) per
855
- // project-guardrails.md, so it must persist across undo. We pass the
856
- // live paymentConfig back in to preserve it.
857
- shopStore.initialiseShopStore({
858
- paymentConfig: shopStore.paymentConfig,
859
- pages: snapshot.pages ?? [],
860
- });
861
-
862
- // 7. Own-store scalar fields surfaced by the brandPageData getter.
863
- if (snapshot.isModifiedAfterTemplateApplied !== undefined) {
864
- this.isModifiedAfterTemplateApplied =
865
- snapshot.isModifiedAfterTemplateApplied;
866
- }
867
-
868
- if (snapshot.brandPageType) {
869
- this.setBrandPageType({ brandPageType: snapshot.brandPageType });
870
- }
871
-
872
- this.faviconUrl = snapshot.faviconUrl;
873
- this.ogImageUrl = snapshot.ogImageUrl;
874
- this.measurementID = snapshot.measurementID;
875
- this.adsenseID = snapshot.adsenseID;
876
- this.ogTitle = snapshot.ogTitle;
877
- this.ogDescription = snapshot.ogDescription;
878
- this.cookieOptions = snapshot.cookieOptions;
879
- this.saveContactSettings = snapshot.saveContactSettings;
880
-
881
- // 8. Re-sync the logo-settings sidebar's `selectedLogoImage` from the
882
- // restored header logo. The preview rehydrates via the page contents,
883
- // but `uiLogoSettings` holds its own copy that drives the sidebar
884
- // "current logo" state — same pattern as `initialiseStoresByBrandPageModelAsync`.
885
- // Also restores the variations grid + template asset for the
886
- // snapshot's draft token (both are sidecars to `brandPageData` —
887
- // BYO upload mutates them out of band).
888
- this._syncLogoSettingsFromSnapshot(snapshot);
889
-
890
- // 9. If the sidebar is editing a content item that no longer exists in
891
- // the restored page, fall back to the Content tab so the user doesn't
892
- // stare at a blank settings panel.
893
- this._fallbackSidebarIfSelectedContentMissing(snapshot);
894
-
895
- // 10. Switch to the page the user was viewing when this entry was
896
- // captured (or the closest still-existing page if the captured
897
- // page was the one being undone-away). The history store does the
898
- // walk-back resolution and passes us a pageId that's guaranteed to
899
- // exist in the restored nav — or undefined if no entry walking
900
- // back has a viable id, in which case we fall through to the
901
- // first/default route. Without this, the page-tabs carousel keeps
902
- // highlighting whatever page slug is in the URL (which can no
903
- // longer match any nav item after a page-add undo) and the
904
- // sidebar's `currentPage` getter silently falls back to page 0.
905
- this._navigateToActivePage(activePageId);
906
- },
907
-
908
- _syncLogoSettingsFromSnapshot(snapshot: BrandPageModel): void {
909
- const logoSettingsStore = useUiLogoSettingsStore();
910
- const editableLogoStore = useUiEditableLogoStore();
911
- const logoStore = useLogoStore();
912
-
913
- const logoImage =
914
- snapshot.brandPageType === BRAND_PAGE_TYPES.default
915
- ? (
916
- snapshot.pages?.[0]?.contents?.find(
917
- (c) => c.type === 'logo',
918
- ) as any
919
- )?.logoImage
920
- : snapshot.pageHeaderDesktop?.logoImage;
921
-
922
- if (!logoImage) return;
923
-
924
- logoSettingsStore.setState({
925
- newState: { selectedLogoImage: logoImage },
926
- });
927
-
928
- const draftToken: string | undefined = logoImage.logoDraftToken;
929
-
930
- if (!draftToken) return;
931
-
932
- // Re-derive `enableEditMode` and the active template asset from the
933
- // logo-store's draft registry. Drafts are append-only for the session,
934
- // and BYO upload stamps `templateAsset` onto the registered draft when
935
- // it lands — so the registered draft is enough to fully restore the
936
- // sidebar's design/upload chooser without storing anything inside
937
- // each history entry. The variations grid is restored from the
938
- // editable-logo store's per-token cache (populated by every
939
- // `updateEditableLogoImageUrlAsync` render).
940
- const draft = logoStore.getLogoDraftAssetByToken(draftToken);
941
-
942
- if (draft) {
943
- editableLogoStore.setEnableEditMode(!draft.isByo);
944
-
945
- if (draft.templateAsset) {
946
- logoStore.setLogoTemplateAsset(draft.templateAsset);
947
- }
948
- }
949
-
950
- editableLogoStore.setLogoDraftToken(draftToken);
951
- editableLogoStore.restoreVariationsForDraftToken(draftToken);
952
- },
953
-
954
- /**
955
- * Restore the active page after a history apply. The id passed in is
956
- * already validated against the restored nav by the history store; if
957
- * it's undefined (no entry walking back had a viable page), fall back
958
- * to the first/default route so the URL, `routeStore.currentRoute.params`,
959
- * `pageNavigation.activePageId`, and the page-tab highlight all line up.
960
- *
961
- * `setCurrentActivePage` is called explicitly in addition to
962
- * `navigateToPageByPageId` because `navigateToPageByPageId` is a no-op
963
- * when the router instance is undefined (e.g. SSR / unit tests), but the
964
- * Pinia state must still be updated so the sidebar's `currentPage`
965
- * getter returns the right page.
966
- */
967
- _navigateToActivePage(activePageId?: string): void {
968
- const pageNavigationStore = usePageNavigationStore();
969
-
970
- let targetPageId = activePageId;
971
-
972
- if (!targetPageId) {
973
- const fallbackRoute = pageNavigationStore.routes.length
974
- ? pageNavigationStore.getFirstOrDefaultRoute()
975
- : undefined;
976
-
977
- targetPageId = fallbackRoute?.pageId;
978
- }
979
-
980
- if (!targetPageId) return;
981
-
982
- pageNavigationStore.setCurrentActivePage({ pageId: targetPageId });
983
- navigateToPageByPageId(targetPageId);
984
- },
985
-
986
- _fallbackSidebarIfSelectedContentMissing(snapshot: BrandPageModel): void {
987
- const contentSettingsStore = useUiContentSettingsStore();
988
- const sideBarStore = useUiSideBarStore();
989
-
990
- const selectedItemId = contentSettingsStore.selectedItemId;
991
-
992
- if (!selectedItemId) return;
993
-
994
- const existingContentIds = new Set<string>();
995
-
996
- snapshot.pages?.forEach((page) => {
997
- page.contents?.forEach((content) => {
998
- if (content?.id) existingContentIds.add(content.id);
999
- });
1000
- });
1001
-
1002
- if (existingContentIds.has(selectedItemId)) return;
1003
-
1004
- // The previously-selected content was undone away. Restore the
1005
- // Content tab's natural landing state — the same shape produced
1006
- // when the user clicks the Content tab fresh — instead of leaving
1007
- // the sidebar stuck rendering `<FocusedContentItemSettings>` for
1008
- // a missing item, which paints a blank panel.
1009
- //
1010
- // `resetMode()` pops the modeStack back to the base `edit` entry
1011
- // (the default landing mode); clearing `selectedItemId` releases
1012
- // the pointer to the now-deleted item.
1013
- sideBarStore.setSelectedTabName({
1014
- tabName: SIDE_BAR_TAB_NAMES.CONTENT_SETTINGS,
1015
- });
1016
- contentSettingsStore.resetMode();
1017
- contentSettingsStore.selectedItemId = null;
1018
- contentSettingsStore.selectedItemFieldName = null;
1019
- contentSettingsStore.selectedItemIndex = null;
1020
- contentSettingsStore.selectedItemValue = null;
1021
738
  },
1022
739
  export(): BrandPageModel {
1023
740
  const data = this.brandPageData;
@@ -14,11 +14,6 @@ export interface LogoDraftAsset {
14
14
  tick: number;
15
15
  isByo: boolean;
16
16
  logoEditUrl?: string;
17
- // The `logoTemplateAsset` active when this draft was created. Stamped at
18
- // upload/setState time so undo/redo can restore the right template token
19
- // + isByo flag from a snapshot's `logoDraftToken` alone — without us
20
- // bloating each history entry with a copy of the asset.
21
- templateAsset?: LogoTemplateAsset;
22
17
  }
23
18
 
24
19
  export interface LogoStoreState {
@@ -34,7 +34,6 @@ export const useBrandPageMakerUserStore = defineStore('brandPageMakerUser', {
34
34
  canUseAITextGenerator: false,
35
35
  canUseGoogleAdsense: false,
36
36
  googleAdsenseEnabled: false,
37
- canUseUndoRedo: true,
38
37
  },
39
38
  emailAddress: '',
40
39
  hasPremiumSubscription: false,
@@ -1145,9 +1145,7 @@
1145
1145
  "quickLinkBar": {
1146
1146
  "myWebPageLabel": "My web page:",
1147
1147
  "copyButton": "Copy",
1148
- "copiedButton": "Copied!",
1149
- "undoLabel": "Undo",
1150
- "redoLabel": "Redo"
1148
+ "copiedButton": "Copied!"
1151
1149
  },
1152
1150
  "saveContactSettings": {
1153
1151
  "saveContentInfo": "Let visitors save your contact information when they view your Digital Business Card",
@@ -15,7 +15,6 @@ export interface UIUserFeatures {
15
15
  canUseAITextGenerator: boolean;
16
16
  canUseGoogleAdsense: boolean;
17
17
  googleAdsenseEnabled: boolean;
18
- canUseUndoRedo: boolean;
19
18
  }
20
19
 
21
20
  export const TEMPLATE_FIRST_PAGE_NO = 1;
@@ -33,7 +33,6 @@ export const Sample = (): any => {
33
33
  canUseGoogleAdsense: false,
34
34
  googleAdsenseEnabled: false,
35
35
  canUseAITextGenerator: true,
36
- canUseUndoRedo: false,
37
36
  };
38
37
 
39
38
  const pinia = setupStoryPinia();
@@ -76,12 +76,6 @@ export default defineComponent({
76
76
  schema: 'https',
77
77
  textIcon: 'globe',
78
78
  textIcons: [] as string[],
79
- // Display-only override for the focused URL input. While a row is
80
- // focused we strip the schema visually but MUST NOT mutate modelValue —
81
- // doing so would land in the store and trigger a spurious undo/redo
82
- // history entry between focus and blur (BP-5506).
83
- focusedIndex: null as number | null,
84
- focusedDisplay: '',
85
79
  }),
86
80
  computed: {
87
81
  items: {
@@ -168,26 +162,15 @@ export default defineComponent({
168
162
  },
169
163
 
170
164
  onFocusOverride(index: number, itemValue: string): void {
165
+ const value = [...this.modelValue];
171
166
  const match = URL_RE.exec(itemValue);
172
167
 
173
168
  if (match) {
174
169
  this.schema = match[1];
175
- this.focusedIndex = index;
176
- this.focusedDisplay = match[2];
177
- } else {
178
- this.focusedIndex = index;
179
- this.focusedDisplay = itemValue;
170
+ value[index][this.valuePropName] = match[2];
180
171
  }
181
172
  },
182
- inputDisplayValue(index: number): string {
183
- return this.focusedIndex === index
184
- ? this.focusedDisplay
185
- : this.itemValue(index);
186
- },
187
173
  onBlurOverride(index: number, itemValue: string): void {
188
- this.focusedIndex = null;
189
- this.focusedDisplay = '';
190
-
191
174
  const value = [...this.modelValue];
192
175
 
193
176
  let isValid = true;
@@ -70,7 +70,7 @@
70
70
  hasError[index],
71
71
  }"
72
72
  :placeholder="placeholderPrefix"
73
- :value="inputDisplayValue(index)"
73
+ :value="itemValue(index)"
74
74
  :data-test-tag="'image-url-text-input'"
75
75
  @focus="onFocusOverride(index, $event.target.value)"
76
76
  @blur="onBlurOverride(index, $event.target.value)"
@@ -1,4 +1,4 @@
1
- import { defineComponent, ref, watch } from 'vue';
1
+ import { defineComponent, ref } from 'vue';
2
2
  import type { PropType } from 'vue';
3
3
  import type { FormField } from '@brand-page';
4
4
  import { formFieldsTr } from '@src/composables/useTranslate';
@@ -49,34 +49,18 @@ export default defineComponent({
49
49
  },
50
50
  ];
51
51
 
52
- const valueWithState = ref(
53
- props.value.map((field) => ({
54
- field,
55
- isCollapsed: true,
56
- isTypeDropdownOpen: false,
57
- })),
58
- );
59
-
60
- // Re-sync when the store-bound value changes externally (e.g. undo/redo
61
- // via history-store). Preserve per-row UI flags (isCollapsed,
62
- // isTypeDropdownOpen) by index so an undo doesn't snap rows back to
63
- // collapsed state.
64
- watch(
65
- () => props.value,
66
- (newVal) => {
67
- valueWithState.value = newVal.map((field, index) => ({
68
- field,
69
- isCollapsed: valueWithState.value[index]?.isCollapsed ?? true,
70
- isTypeDropdownOpen:
71
- valueWithState.value[index]?.isTypeDropdownOpen ?? false,
72
- }));
73
- },
74
- );
75
-
76
52
  return {
77
53
  formFieldsTr,
78
54
  typeList,
79
- valueWithState,
55
+ valueWithState: ref(
56
+ props.value.map((field) => {
57
+ return {
58
+ field: field,
59
+ isCollapsed: true,
60
+ isTypeDropdownOpen: false,
61
+ };
62
+ }),
63
+ ),
80
64
  };
81
65
  },
82
66
  methods: {