@accelerated-agency/visual-editor 0.2.5 → 0.2.6

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 (3) hide show
  1. package/dist/vite.cjs +140 -2
  2. package/dist/vite.js +140 -2
  3. package/package.json +1 -1
package/dist/vite.cjs CHANGED
@@ -627,7 +627,7 @@ select.pr-inp{cursor:pointer;background:#fff}
627
627
  </div>
628
628
  <!-- btn-close: hidden visually, kept for JS event listener -->
629
629
  <button id="btn-close" style="display:none" title="Close editor"></button>
630
- <button class="tb-sim-btn" id="btn-simulate"><i class="bi bi-lightning-charge-fill"></i> Simulate</button>
630
+ <button class="tb-sim-btn" id="btn-simulate" onclick="simulateExperiment()"><i class="bi bi-lightning-charge-fill"></i> Simulate</button>
631
631
  <button class="tb-fin-btn" id="btn-save">Finalize</button>
632
632
  </div>
633
633
 
@@ -914,6 +914,52 @@ function send(type, payload) {
914
914
  window.parent.postMessage({ channel: CHANNEL, type: type, payload: payload || {} }, '*');
915
915
  }
916
916
 
917
+ function generatePreviewUrlString(args) {
918
+ var baseUrl = (args && args.url) || '';
919
+ var test = (args && args.test) || {};
920
+ var variation = (args && args.variation) || {};
921
+ if (!baseUrl) return '';
922
+ var testId = test.iid || test.experimentId || test._id || '';
923
+ var variationId = variation.iid || variation._id || '';
924
+ var cId = String(testId || '') + '_' + String(variationId || '');
925
+ var hasQueryParams = String(baseUrl).indexOf('?') >= 0;
926
+ return (
927
+ baseUrl +
928
+ (hasQueryParams ? '&' : '?') +
929
+ 'codebase_debug=true&cId=' +
930
+ encodeURIComponent(cId)
931
+ );
932
+ }
933
+
934
+ function simulateExperiment() {
935
+ var test = experimentData || {};
936
+ var activeVariation = null;
937
+ if (Array.isArray(variations) && variations.length) {
938
+ activeVariation =
939
+ variations.find(function(v) { return v && v._id === activeVarId; }) ||
940
+ variations[0];
941
+ }
942
+ var targetUrl =
943
+ (Array.isArray(test.urltargeting) && test.urltargeting[0]) ||
944
+ test.pageUrl ||
945
+ (test.metadata_1 && test.metadata_1.editor_url) ||
946
+ '';
947
+ var url = generatePreviewUrlString({
948
+ url: targetUrl,
949
+ test: test,
950
+ variation: activeVariation || {},
951
+ });
952
+ if (!url) {
953
+ console.warn('[V2] simulateExperiment: missing target URL');
954
+ return;
955
+ }
956
+ try {
957
+ window.open(url, '_blank');
958
+ } catch(err) {
959
+ console.warn('[V2] simulateExperiment:', err);
960
+ }
961
+ }
962
+
917
963
  window.addEventListener('message', function(e) {
918
964
  if (!e.data || e.data.channel !== CHANNEL) return;
919
965
  switch (e.data.type) {
@@ -965,6 +1011,13 @@ var changeObserver = null;
965
1011
  var changeObserverDoc = null;
966
1012
  /** Incremented while applying changesets / selection chrome so MutationObserver does not mark dirty */
967
1013
  var suppressIframeMutationDirty = 0;
1014
+ /** Throttled reconcile timer to keep DOM aligned with saved changesets. */
1015
+ var consistencyReconcileTimer = null;
1016
+ /** Background watchdog for late client-side re-renders that wipe applied changesets. */
1017
+ var consistencyWatchTimer = null;
1018
+ var consistencyWatchDoc = null;
1019
+ var consistencyWatchTicks = 0;
1020
+ var CONSISTENCY_WATCH_MAX_TICKS = 160;
968
1021
  /** { doc, onRS } \u2014 iframe document readystate until complete */
969
1022
  var iframeDocLoadingListeners = null;
970
1023
  // Each entry: {selector, label, cssProp, value, targetEl}
@@ -1948,9 +2001,71 @@ function clearPendingGranularChangesets() {
1948
2001
  }
1949
2002
  }
1950
2003
 
2004
+ function stopConsistencyWatchdog() {
2005
+ if (consistencyWatchTimer) {
2006
+ clearInterval(consistencyWatchTimer);
2007
+ consistencyWatchTimer = null;
2008
+ }
2009
+ consistencyWatchDoc = null;
2010
+ consistencyWatchTicks = 0;
2011
+ }
2012
+
2013
+ function runConsistencyReconcile() {
2014
+ if (!activeVarId) return;
2015
+ var iframe = document.getElementById('iframeId');
2016
+ var doc = iframe && iframe.contentDocument;
2017
+ if (!doc || !doc.body) return;
2018
+ var variation = variations.find(function(v) { return v._id === activeVarId; });
2019
+ var cs = parseVariationChangesets(variation);
2020
+ if (!cs.length || changesetsHaveBodySnapshot(cs)) return;
2021
+ var granular = filterGranularChangesetEntries(cs);
2022
+ var unresolved = countUnresolvedGranularSelectors(doc, granular);
2023
+ if (unresolved > 0 || changesetListHasStructural(cs)) {
2024
+ reapplyActiveVariationGranular(doc);
2025
+ registerPendingGranularChangesets(cs, doc);
2026
+ }
2027
+ }
2028
+
2029
+ function scheduleConsistencyReconcile() {
2030
+ if (consistencyReconcileTimer) return;
2031
+ consistencyReconcileTimer = setTimeout(function() {
2032
+ consistencyReconcileTimer = null;
2033
+ runConsistencyReconcile();
2034
+ }, 80);
2035
+ }
2036
+
2037
+ function startConsistencyWatchdog(doc) {
2038
+ if (!doc || !doc.body) return;
2039
+ if (consistencyWatchDoc === doc && consistencyWatchTimer) return;
2040
+ stopConsistencyWatchdog();
2041
+ consistencyWatchDoc = doc;
2042
+ consistencyWatchTicks = 0;
2043
+ consistencyWatchTimer = setInterval(function() {
2044
+ if (consistencyWatchDoc !== doc) {
2045
+ stopConsistencyWatchdog();
2046
+ return;
2047
+ }
2048
+ var iframe = document.getElementById('iframeId');
2049
+ if (!iframe || iframe.contentDocument !== doc || !doc.body) {
2050
+ stopConsistencyWatchdog();
2051
+ return;
2052
+ }
2053
+ consistencyWatchTicks += 1;
2054
+ scheduleConsistencyReconcile();
2055
+ if (consistencyWatchTicks >= CONSISTENCY_WATCH_MAX_TICKS) {
2056
+ stopConsistencyWatchdog();
2057
+ }
2058
+ }, 400);
2059
+ }
2060
+
1951
2061
  function resetIframeBindings() {
1952
2062
  detachIframeLoadingListeners();
1953
2063
  stopIframeContentApplyWatcher();
2064
+ stopConsistencyWatchdog();
2065
+ if (consistencyReconcileTimer) {
2066
+ clearTimeout(consistencyReconcileTimer);
2067
+ consistencyReconcileTimer = null;
2068
+ }
1954
2069
  appliedChangesetSnapshots = {};
1955
2070
  appliedStructuralChangesetKeys = {};
1956
2071
  clickAttachDoc = null;
@@ -3564,10 +3679,31 @@ function attachChangeObserver() {
3564
3679
  changeObserver = null;
3565
3680
  changeObserverDoc = null;
3566
3681
  }
3567
- changeObserver = new MutationObserver(function() {
3682
+ changeObserver = new MutationObserver(function(mutations) {
3568
3683
  // Dirty state is derived from changesets baseline + stateChanges (not raw DOM mutations).
3684
+ var bodyReplaced = false;
3685
+ for (var mi = 0; mi < mutations.length; mi++) {
3686
+ var m = mutations[mi];
3687
+ if (
3688
+ m &&
3689
+ m.type === 'childList' &&
3690
+ m.target === doc.body &&
3691
+ m.addedNodes &&
3692
+ m.removedNodes &&
3693
+ m.addedNodes.length > 0 &&
3694
+ m.removedNodes.length > 0
3695
+ ) {
3696
+ bodyReplaced = true;
3697
+ break;
3698
+ }
3699
+ }
3700
+ if (bodyReplaced) {
3701
+ // Page JS replaced body children; allow structural rows (insert/reorder) to apply again.
3702
+ appliedStructuralChangesetKeys = {};
3703
+ }
3569
3704
  scheduleDomTreeRefresh();
3570
3705
  scheduleGranularChangesetReapply();
3706
+ scheduleConsistencyReconcile();
3571
3707
  });
3572
3708
  changeObserver.observe(doc.body, {
3573
3709
  childList: true, subtree: true, attributes: true, characterData: true
@@ -3597,6 +3733,8 @@ function syncIframeInteractions(reason) {
3597
3733
  attachClickHandler();
3598
3734
  attachDragReposition();
3599
3735
  attachChangeObserver();
3736
+ startConsistencyWatchdog(doc);
3737
+ scheduleConsistencyReconcile();
3600
3738
  bindSelectionToolbarScroll();
3601
3739
  var inp = document.getElementById('comp-search');
3602
3740
  renderDomTree(inp ? inp.value : '');
package/dist/vite.js CHANGED
@@ -619,7 +619,7 @@ select.pr-inp{cursor:pointer;background:#fff}
619
619
  </div>
620
620
  <!-- btn-close: hidden visually, kept for JS event listener -->
621
621
  <button id="btn-close" style="display:none" title="Close editor"></button>
622
- <button class="tb-sim-btn" id="btn-simulate"><i class="bi bi-lightning-charge-fill"></i> Simulate</button>
622
+ <button class="tb-sim-btn" id="btn-simulate" onclick="simulateExperiment()"><i class="bi bi-lightning-charge-fill"></i> Simulate</button>
623
623
  <button class="tb-fin-btn" id="btn-save">Finalize</button>
624
624
  </div>
625
625
 
@@ -906,6 +906,52 @@ function send(type, payload) {
906
906
  window.parent.postMessage({ channel: CHANNEL, type: type, payload: payload || {} }, '*');
907
907
  }
908
908
 
909
+ function generatePreviewUrlString(args) {
910
+ var baseUrl = (args && args.url) || '';
911
+ var test = (args && args.test) || {};
912
+ var variation = (args && args.variation) || {};
913
+ if (!baseUrl) return '';
914
+ var testId = test.iid || test.experimentId || test._id || '';
915
+ var variationId = variation.iid || variation._id || '';
916
+ var cId = String(testId || '') + '_' + String(variationId || '');
917
+ var hasQueryParams = String(baseUrl).indexOf('?') >= 0;
918
+ return (
919
+ baseUrl +
920
+ (hasQueryParams ? '&' : '?') +
921
+ 'codebase_debug=true&cId=' +
922
+ encodeURIComponent(cId)
923
+ );
924
+ }
925
+
926
+ function simulateExperiment() {
927
+ var test = experimentData || {};
928
+ var activeVariation = null;
929
+ if (Array.isArray(variations) && variations.length) {
930
+ activeVariation =
931
+ variations.find(function(v) { return v && v._id === activeVarId; }) ||
932
+ variations[0];
933
+ }
934
+ var targetUrl =
935
+ (Array.isArray(test.urltargeting) && test.urltargeting[0]) ||
936
+ test.pageUrl ||
937
+ (test.metadata_1 && test.metadata_1.editor_url) ||
938
+ '';
939
+ var url = generatePreviewUrlString({
940
+ url: targetUrl,
941
+ test: test,
942
+ variation: activeVariation || {},
943
+ });
944
+ if (!url) {
945
+ console.warn('[V2] simulateExperiment: missing target URL');
946
+ return;
947
+ }
948
+ try {
949
+ window.open(url, '_blank');
950
+ } catch(err) {
951
+ console.warn('[V2] simulateExperiment:', err);
952
+ }
953
+ }
954
+
909
955
  window.addEventListener('message', function(e) {
910
956
  if (!e.data || e.data.channel !== CHANNEL) return;
911
957
  switch (e.data.type) {
@@ -957,6 +1003,13 @@ var changeObserver = null;
957
1003
  var changeObserverDoc = null;
958
1004
  /** Incremented while applying changesets / selection chrome so MutationObserver does not mark dirty */
959
1005
  var suppressIframeMutationDirty = 0;
1006
+ /** Throttled reconcile timer to keep DOM aligned with saved changesets. */
1007
+ var consistencyReconcileTimer = null;
1008
+ /** Background watchdog for late client-side re-renders that wipe applied changesets. */
1009
+ var consistencyWatchTimer = null;
1010
+ var consistencyWatchDoc = null;
1011
+ var consistencyWatchTicks = 0;
1012
+ var CONSISTENCY_WATCH_MAX_TICKS = 160;
960
1013
  /** { doc, onRS } \u2014 iframe document readystate until complete */
961
1014
  var iframeDocLoadingListeners = null;
962
1015
  // Each entry: {selector, label, cssProp, value, targetEl}
@@ -1940,9 +1993,71 @@ function clearPendingGranularChangesets() {
1940
1993
  }
1941
1994
  }
1942
1995
 
1996
+ function stopConsistencyWatchdog() {
1997
+ if (consistencyWatchTimer) {
1998
+ clearInterval(consistencyWatchTimer);
1999
+ consistencyWatchTimer = null;
2000
+ }
2001
+ consistencyWatchDoc = null;
2002
+ consistencyWatchTicks = 0;
2003
+ }
2004
+
2005
+ function runConsistencyReconcile() {
2006
+ if (!activeVarId) return;
2007
+ var iframe = document.getElementById('iframeId');
2008
+ var doc = iframe && iframe.contentDocument;
2009
+ if (!doc || !doc.body) return;
2010
+ var variation = variations.find(function(v) { return v._id === activeVarId; });
2011
+ var cs = parseVariationChangesets(variation);
2012
+ if (!cs.length || changesetsHaveBodySnapshot(cs)) return;
2013
+ var granular = filterGranularChangesetEntries(cs);
2014
+ var unresolved = countUnresolvedGranularSelectors(doc, granular);
2015
+ if (unresolved > 0 || changesetListHasStructural(cs)) {
2016
+ reapplyActiveVariationGranular(doc);
2017
+ registerPendingGranularChangesets(cs, doc);
2018
+ }
2019
+ }
2020
+
2021
+ function scheduleConsistencyReconcile() {
2022
+ if (consistencyReconcileTimer) return;
2023
+ consistencyReconcileTimer = setTimeout(function() {
2024
+ consistencyReconcileTimer = null;
2025
+ runConsistencyReconcile();
2026
+ }, 80);
2027
+ }
2028
+
2029
+ function startConsistencyWatchdog(doc) {
2030
+ if (!doc || !doc.body) return;
2031
+ if (consistencyWatchDoc === doc && consistencyWatchTimer) return;
2032
+ stopConsistencyWatchdog();
2033
+ consistencyWatchDoc = doc;
2034
+ consistencyWatchTicks = 0;
2035
+ consistencyWatchTimer = setInterval(function() {
2036
+ if (consistencyWatchDoc !== doc) {
2037
+ stopConsistencyWatchdog();
2038
+ return;
2039
+ }
2040
+ var iframe = document.getElementById('iframeId');
2041
+ if (!iframe || iframe.contentDocument !== doc || !doc.body) {
2042
+ stopConsistencyWatchdog();
2043
+ return;
2044
+ }
2045
+ consistencyWatchTicks += 1;
2046
+ scheduleConsistencyReconcile();
2047
+ if (consistencyWatchTicks >= CONSISTENCY_WATCH_MAX_TICKS) {
2048
+ stopConsistencyWatchdog();
2049
+ }
2050
+ }, 400);
2051
+ }
2052
+
1943
2053
  function resetIframeBindings() {
1944
2054
  detachIframeLoadingListeners();
1945
2055
  stopIframeContentApplyWatcher();
2056
+ stopConsistencyWatchdog();
2057
+ if (consistencyReconcileTimer) {
2058
+ clearTimeout(consistencyReconcileTimer);
2059
+ consistencyReconcileTimer = null;
2060
+ }
1946
2061
  appliedChangesetSnapshots = {};
1947
2062
  appliedStructuralChangesetKeys = {};
1948
2063
  clickAttachDoc = null;
@@ -3556,10 +3671,31 @@ function attachChangeObserver() {
3556
3671
  changeObserver = null;
3557
3672
  changeObserverDoc = null;
3558
3673
  }
3559
- changeObserver = new MutationObserver(function() {
3674
+ changeObserver = new MutationObserver(function(mutations) {
3560
3675
  // Dirty state is derived from changesets baseline + stateChanges (not raw DOM mutations).
3676
+ var bodyReplaced = false;
3677
+ for (var mi = 0; mi < mutations.length; mi++) {
3678
+ var m = mutations[mi];
3679
+ if (
3680
+ m &&
3681
+ m.type === 'childList' &&
3682
+ m.target === doc.body &&
3683
+ m.addedNodes &&
3684
+ m.removedNodes &&
3685
+ m.addedNodes.length > 0 &&
3686
+ m.removedNodes.length > 0
3687
+ ) {
3688
+ bodyReplaced = true;
3689
+ break;
3690
+ }
3691
+ }
3692
+ if (bodyReplaced) {
3693
+ // Page JS replaced body children; allow structural rows (insert/reorder) to apply again.
3694
+ appliedStructuralChangesetKeys = {};
3695
+ }
3561
3696
  scheduleDomTreeRefresh();
3562
3697
  scheduleGranularChangesetReapply();
3698
+ scheduleConsistencyReconcile();
3563
3699
  });
3564
3700
  changeObserver.observe(doc.body, {
3565
3701
  childList: true, subtree: true, attributes: true, characterData: true
@@ -3589,6 +3725,8 @@ function syncIframeInteractions(reason) {
3589
3725
  attachClickHandler();
3590
3726
  attachDragReposition();
3591
3727
  attachChangeObserver();
3728
+ startConsistencyWatchdog(doc);
3729
+ scheduleConsistencyReconcile();
3592
3730
  bindSelectionToolbarScroll();
3593
3731
  var inp = document.getElementById('comp-search');
3594
3732
  renderDomTree(inp ? inp.value : '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accelerated-agency/visual-editor",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "private": false,
5
5
  "description": "Conversion visual editor as a reusable React package",
6
6
  "type": "module",