@accelerated-agency/visual-editor 0.2.1 → 0.2.2

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 (2) hide show
  1. package/dist/index.js +105 -70
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,6 +5,11 @@ import { persist } from 'zustand/middleware';
5
5
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
6
  import { HexColorPicker } from 'react-colorful';
7
7
 
8
+ var PERSIST_NAME_PREFIX = "conversion-editor-variations-v3-";
9
+ function variationsPersistStorageName(experimentId) {
10
+ const id = experimentId?.trim();
11
+ return `${PERSIST_NAME_PREFIX}${id && id.length > 0 ? id : "standalone"}`;
12
+ }
8
13
  var VARIATION_COLORS = [
9
14
  "#0084D1",
10
15
  "#F54A00",
@@ -77,16 +82,12 @@ var useVariationsStore = create()(
77
82
  }));
78
83
  },
79
84
  setActiveVariation: (id) => set({ activeVariationId: id }),
80
- addMutationToActive: (mutation) => set((s) => {
81
- console.log("addMutationToActive", s.activeVariationId, mutation);
82
- return {
83
- variations: s.variations.map(
84
- (v) => v.id === s.activeVariationId ? { ...v, mutations: [...v.mutations, mutation] } : v
85
- ),
86
- // Clear redo stack for active variation on new mutation
87
- redoStacks: { ...s.redoStacks, [s.activeVariationId]: [] }
88
- };
89
- }),
85
+ addMutationToActive: (mutation) => set((s) => ({
86
+ variations: s.variations.map(
87
+ (v) => v.id === s.activeVariationId ? { ...v, mutations: [...v.mutations, mutation] } : v
88
+ ),
89
+ redoStacks: { ...s.redoStacks, [s.activeVariationId]: [] }
90
+ })),
90
91
  removeLastMutationFromActive: () => set((s) => {
91
92
  const active = s.variations.find((v) => v.id === s.activeVariationId);
92
93
  const removed = active?.mutations[active.mutations.length - 1];
@@ -149,7 +150,8 @@ var useVariationsStore = create()(
149
150
  }
150
151
  }),
151
152
  {
152
- name: "conversion-editor-variations",
153
+ name: variationsPersistStorageName(void 0),
154
+ skipHydration: true,
153
155
  partialize: (state) => ({
154
156
  variations: state.variations,
155
157
  activeVariationId: state.activeVariationId
@@ -157,6 +159,15 @@ var useVariationsStore = create()(
157
159
  }
158
160
  )
159
161
  );
162
+ async function hydrateVariationsFromStorage(experimentId) {
163
+ useVariationsStore.persist.setOptions({
164
+ name: variationsPersistStorageName(experimentId)
165
+ });
166
+ try {
167
+ await useVariationsStore.persist.rehydrate();
168
+ } catch {
169
+ }
170
+ }
160
171
  function TopBar({
161
172
  connectionStatus,
162
173
  onLoadUrl,
@@ -4078,6 +4089,9 @@ function sendToPlatform(type, payload) {
4078
4089
  data: { channel: PLATFORM_CHANNEL, type, payload }
4079
4090
  }));
4080
4091
  }
4092
+ function experimentIframeContextKey(exp) {
4093
+ return [exp.experimentId ?? "", exp.pageUrl ?? "", exp.editorPassword ?? ""].join("");
4094
+ }
4081
4095
  function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
4082
4096
  const [url, setUrl] = useState("");
4083
4097
  const [password, setPassword] = useState("");
@@ -4090,7 +4104,8 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
4090
4104
  );
4091
4105
  const [experimentData, setExperimentData] = useState(null);
4092
4106
  const experimentDataRef = useRef(null);
4093
- const lastInitialExperimentKeyRef = useRef("");
4107
+ const lastAppliedRunKeyRef = useRef("");
4108
+ const iframeContextKeyRef = useRef("");
4094
4109
  useEffect(() => {
4095
4110
  experimentDataRef.current = experimentData;
4096
4111
  }, [experimentData]);
@@ -4108,7 +4123,6 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
4108
4123
  const clearAll = useVariationsStore((s) => s.clearAll);
4109
4124
  const setSelectedElement = useMutationsStore((s) => s.setSelectedElement);
4110
4125
  const selectedElement = useMutationsStore((s) => s.selectedElement);
4111
- const activeVariationId = useVariationsStore((s) => s.activeVariationId);
4112
4126
  const variations = useVariationsStore((s) => s.variations);
4113
4127
  const removeLastMutationFromActive = useVariationsStore((s) => s.removeLastMutationFromActive);
4114
4128
  const removeMutationsForSelector = useVariationsStore((s) => s.removeMutationsForSelector);
@@ -4159,7 +4173,6 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
4159
4173
  }, [embedded, experimentData, toast, buildPlatformVariations]);
4160
4174
  const pingIntervalRef = useRef(null);
4161
4175
  const pongTimeoutRef = useRef(null);
4162
- const syncDebounceRef = useRef(null);
4163
4176
  const stopHeartbeat = useCallback(() => {
4164
4177
  if (pingIntervalRef.current) {
4165
4178
  clearInterval(pingIntervalRef.current);
@@ -4193,15 +4206,24 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
4193
4206
  },
4194
4207
  [stopHeartbeat, clearAll, setSelectedElement]
4195
4208
  );
4209
+ useEffect(() => {
4210
+ if (initialExperiment) return;
4211
+ let cancelled = false;
4212
+ void (async () => {
4213
+ await hydrateVariationsFromStorage(void 0);
4214
+ if (cancelled) return;
4215
+ })();
4216
+ return () => {
4217
+ cancelled = true;
4218
+ };
4219
+ }, [initialExperiment]);
4196
4220
  useEffect(() => {
4197
4221
  if (!initialExperiment) return;
4198
- const key = JSON.stringify(initialExperiment);
4199
- if (lastInitialExperimentKeyRef.current === key) return;
4200
- lastInitialExperimentKeyRef.current = key;
4201
- setExperimentData(initialExperiment);
4202
- if (initialExperiment.pageUrl) {
4203
- handleLoadUrl(initialExperiment.pageUrl, initialExperiment.editorPassword || void 0);
4204
- }
4222
+ const runKey = JSON.stringify(initialExperiment);
4223
+ if (lastAppliedRunKeyRef.current === runKey) return;
4224
+ const ctx = experimentIframeContextKey(initialExperiment);
4225
+ const sameIframeContext = iframeContextKeyRef.current !== "" && iframeContextKeyRef.current === ctx;
4226
+ let cancelled = false;
4205
4227
  const sourceVariations = Array.isArray(initialExperiment.variations) ? initialExperiment.variations : [];
4206
4228
  const editorVariations = sourceVariations.map((v, idx) => {
4207
4229
  let mutations = [];
@@ -4221,7 +4243,31 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
4221
4243
  mutations
4222
4244
  };
4223
4245
  });
4224
- loadExperimentVariations(editorVariations);
4246
+ setExperimentData(initialExperiment);
4247
+ if (sameIframeContext) {
4248
+ void (async () => {
4249
+ await hydrateVariationsFromStorage(initialExperiment.experimentId);
4250
+ if (cancelled) return;
4251
+ loadExperimentVariations(editorVariations);
4252
+ lastAppliedRunKeyRef.current = runKey;
4253
+ })();
4254
+ return () => {
4255
+ cancelled = true;
4256
+ };
4257
+ }
4258
+ if (initialExperiment.pageUrl) {
4259
+ handleLoadUrl(initialExperiment.pageUrl, initialExperiment.editorPassword || void 0);
4260
+ iframeContextKeyRef.current = ctx;
4261
+ }
4262
+ void (async () => {
4263
+ await hydrateVariationsFromStorage(initialExperiment.experimentId);
4264
+ if (cancelled) return;
4265
+ loadExperimentVariations(editorVariations);
4266
+ lastAppliedRunKeyRef.current = runKey;
4267
+ })();
4268
+ return () => {
4269
+ cancelled = true;
4270
+ };
4225
4271
  }, [initialExperiment, handleLoadUrl, loadExperimentVariations]);
4226
4272
  const handleBridgeReady = useCallback(() => {
4227
4273
  setConnectionStatus("connected");
@@ -4251,33 +4297,44 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
4251
4297
  switch (msg.type) {
4252
4298
  case "load-experiment": {
4253
4299
  const data = msg.payload;
4254
- setExperimentData(data);
4255
4300
  experimentDataRef.current = data;
4256
- if (data.pageUrl && !msg.payload?.skipUrlReload) {
4257
- handleLoadUrl(data.pageUrl, data.editorPassword || void 0);
4258
- }
4259
- if (Array.isArray(data.variations)) {
4260
- const editorVariations = data.variations.map((v, idx) => {
4261
- let mutations = [];
4262
- try {
4263
- const chainSets = JSON.parse(v.changesets || "[]");
4264
- if (Array.isArray(chainSets) && chainSets.length > 0) {
4265
- mutations = convertChainSetsToMutations(chainSets);
4266
- }
4267
- } catch {
4301
+ const editorVariations = Array.isArray(data.variations) ? data.variations.map((v, idx) => {
4302
+ let mutations = [];
4303
+ try {
4304
+ const chainSets = JSON.parse(v.changesets || "[]");
4305
+ if (Array.isArray(chainSets) && chainSets.length > 0) {
4306
+ mutations = convertChainSetsToMutations(chainSets);
4268
4307
  }
4269
- return {
4270
- id: v._id || `v_${idx}`,
4271
- platformIid: v.iid,
4272
- name: v.name,
4273
- isControl: v.baseline || false,
4274
- traffic_allocation: v.traffic_allocation,
4275
- mutations
4276
- };
4277
- });
4278
- loadExperimentVariations(editorVariations);
4308
+ } catch {
4309
+ }
4310
+ return {
4311
+ id: v._id || `v_${idx}`,
4312
+ platformIid: v.iid,
4313
+ name: v.name,
4314
+ isControl: v.baseline || false,
4315
+ traffic_allocation: v.traffic_allocation,
4316
+ mutations
4317
+ };
4318
+ }) : [];
4319
+ setExperimentData(data);
4320
+ const ctx = experimentIframeContextKey(data);
4321
+ let reloadedIframe = false;
4322
+ if (data.pageUrl && !data.skipUrlReload) {
4323
+ if (iframeContextKeyRef.current !== ctx) {
4324
+ handleLoadUrl(data.pageUrl, data.editorPassword || void 0);
4325
+ iframeContextKeyRef.current = ctx;
4326
+ reloadedIframe = true;
4327
+ }
4279
4328
  }
4280
- toast(`Loaded experiment: ${data.name || "Untitled"}`, "info");
4329
+ void (async () => {
4330
+ await hydrateVariationsFromStorage(data.experimentId);
4331
+ if (Array.isArray(data.variations)) {
4332
+ loadExperimentVariations(editorVariations);
4333
+ }
4334
+ if (reloadedIframe) {
4335
+ toast(`Loaded experiment: ${data.name || "Untitled"}`, "info");
4336
+ }
4337
+ })();
4281
4338
  break;
4282
4339
  }
4283
4340
  case "request-save": {
@@ -4351,27 +4408,6 @@ function EditorShell({ initialExperiment, embeddedMode, proxyBaseUrl }) {
4351
4408
  useEffect(() => {
4352
4409
  sendToBridge({ type: "setMode", mode: interactionMode });
4353
4410
  }, [interactionMode]);
4354
- useEffect(() => {
4355
- if (syncDebounceRef.current) {
4356
- clearTimeout(syncDebounceRef.current);
4357
- syncDebounceRef.current = null;
4358
- }
4359
- if (connectionStatus !== "connected") return;
4360
- syncDebounceRef.current = setTimeout(() => {
4361
- const currentActiveMutations = useVariationsStore.getState().getActiveMutations();
4362
- sendToBridge({ type: "clearAllMutations" });
4363
- if (currentActiveMutations.length > 0) {
4364
- sendToBridge({ type: "applyMutationBatch", mutations: currentActiveMutations });
4365
- }
4366
- syncDebounceRef.current = null;
4367
- }, 350);
4368
- return () => {
4369
- if (syncDebounceRef.current) {
4370
- clearTimeout(syncDebounceRef.current);
4371
- syncDebounceRef.current = null;
4372
- }
4373
- };
4374
- }, [connectionStatus, activeVariationId, experimentData?.experimentId]);
4375
4411
  useEffect(() => {
4376
4412
  const handler = (e) => {
4377
4413
  const meta = e.metaKey || e.ctrlKey;
@@ -4574,7 +4610,7 @@ function PlatformVisualEditor({
4574
4610
  renderLoading,
4575
4611
  renderError
4576
4612
  }) {
4577
- console.log(experiment);
4613
+ console.log("experiment", experiment);
4578
4614
  const [editorReady, setEditorReady] = useState(false);
4579
4615
  const [dirty, setDirty] = useState(false);
4580
4616
  const dirtyRef = useRef(false);
@@ -4788,7 +4824,6 @@ function PlatformVisualEditorV2({
4788
4824
  renderLoading,
4789
4825
  renderError
4790
4826
  }) {
4791
- console.log(experiment);
4792
4827
  const iframeRef = useRef(null);
4793
4828
  const [editorReady, setEditorReady] = useState(false);
4794
4829
  const [dirty, setDirty] = useState(false);
@@ -4978,4 +5013,4 @@ function PlatformVisualEditorV2({
4978
5013
  ] });
4979
5014
  }
4980
5015
 
4981
- export { EditorShell, PlatformVisualEditor, PlatformVisualEditorV2, ToastProvider, useToast };
5016
+ export { EditorShell, PlatformVisualEditor, PlatformVisualEditorV2, ToastProvider, hydrateVariationsFromStorage, useToast, variationsPersistStorageName };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accelerated-agency/visual-editor",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "description": "Conversion visual editor as a reusable React package",
6
6
  "type": "module",