@accelerated-agency/visual-editor 0.6.1 → 0.7.0
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.
- package/dist/images/cro-sections/cro-cta-block.png +0 -0
- package/dist/images/cro-sections/cro-exit-intent-bar.png +0 -0
- package/dist/images/cro-sections/cro-feature-grid.png +0 -0
- package/dist/images/cro-sections/cro-hero.png +0 -0
- package/dist/images/cro-sections/cro-pricing-table.png +0 -0
- package/dist/images/cro-sections/cro-social-proof.png +0 -0
- package/dist/images/cro-sections/cro-trust-badges.png +0 -0
- package/dist/images/cro-sections/cro-urgency-banner.png +0 -0
- package/dist/index.js +171 -4
- package/dist/vite.cjs +25049 -5753
- package/dist/vite.js +25049 -5753
- package/package.json +8 -4
- package/src/images/cro-sections/cro-cta-block.png +0 -0
- package/src/images/cro-sections/cro-exit-intent-bar.png +0 -0
- package/src/images/cro-sections/cro-feature-grid.png +0 -0
- package/src/images/cro-sections/cro-hero.png +0 -0
- package/src/images/cro-sections/cro-pricing-table.png +0 -0
- package/src/images/cro-sections/cro-social-proof.png +0 -0
- package/src/images/cro-sections/cro-trust-badges.png +0 -0
- package/src/images/cro-sections/cro-urgency-banner.png +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -4801,7 +4801,110 @@ function PlatformVisualEditor({
|
|
|
4801
4801
|
/* @__PURE__ */ jsx("div", { className: editorClassName, children: /* @__PURE__ */ jsx(ToastProvider, { children: /* @__PURE__ */ jsx(EditorShell, { initialExperiment: experiment, embeddedMode: true, proxyBaseUrl }) }) })
|
|
4802
4802
|
] });
|
|
4803
4803
|
}
|
|
4804
|
+
|
|
4805
|
+
// src/lib/uploadImageMessage.ts
|
|
4806
|
+
var VISUAL_EDITOR_MAX_UPLOAD_BYTES = 1e7;
|
|
4807
|
+
var ALLOWED_TYPES = /* @__PURE__ */ new Set([
|
|
4808
|
+
"image/jpeg",
|
|
4809
|
+
"image/png",
|
|
4810
|
+
"image/gif",
|
|
4811
|
+
"image/webp"
|
|
4812
|
+
]);
|
|
4813
|
+
var EXT_TO_TYPE = {
|
|
4814
|
+
jpg: "image/jpeg",
|
|
4815
|
+
jpeg: "image/jpeg",
|
|
4816
|
+
png: "image/png",
|
|
4817
|
+
gif: "image/gif",
|
|
4818
|
+
webp: "image/webp"
|
|
4819
|
+
};
|
|
4820
|
+
function uploadImageErrorMessage(err, fallback = "The upload was refused") {
|
|
4821
|
+
if (err instanceof Error && err.message) return err.message;
|
|
4822
|
+
if (typeof err === "string" && err.trim()) return err;
|
|
4823
|
+
return fallback;
|
|
4824
|
+
}
|
|
4825
|
+
function fileFromUploadImagePayload(payload) {
|
|
4826
|
+
if (!payload || typeof payload !== "object") {
|
|
4827
|
+
throw new Error("No image was sent");
|
|
4828
|
+
}
|
|
4829
|
+
const existing = asBlob(payload.file);
|
|
4830
|
+
const name = sanitizeName(payload.name, existing instanceof File ? existing.name : "");
|
|
4831
|
+
const type = normalizeType(
|
|
4832
|
+
typeof payload.type === "string" ? payload.type : existing?.type,
|
|
4833
|
+
name
|
|
4834
|
+
);
|
|
4835
|
+
const bytes = existing ? void 0 : bytesFromUnknown(payload.bytes !== void 0 ? payload.bytes : payload.data);
|
|
4836
|
+
if (!existing && !bytes) {
|
|
4837
|
+
throw new Error("No image bytes were sent");
|
|
4838
|
+
}
|
|
4839
|
+
const size = existing?.size ?? bytes.byteLength;
|
|
4840
|
+
if (typeof payload.size === "number" && payload.size > VISUAL_EDITOR_MAX_UPLOAD_BYTES) {
|
|
4841
|
+
throw new Error("Image is larger than 10MB");
|
|
4842
|
+
}
|
|
4843
|
+
if (size > VISUAL_EDITOR_MAX_UPLOAD_BYTES) {
|
|
4844
|
+
throw new Error("Image is larger than 10MB");
|
|
4845
|
+
}
|
|
4846
|
+
if (!ALLOWED_TYPES.has(type)) {
|
|
4847
|
+
throw new Error("Use a JPEG, PNG, GIF, or WebP image");
|
|
4848
|
+
}
|
|
4849
|
+
if (existing instanceof File) {
|
|
4850
|
+
if (existing.type && !ALLOWED_TYPES.has(normalizeType(existing.type, existing.name))) {
|
|
4851
|
+
throw new Error("Use a JPEG, PNG, GIF, or WebP image");
|
|
4852
|
+
}
|
|
4853
|
+
return existing;
|
|
4854
|
+
}
|
|
4855
|
+
if (existing) {
|
|
4856
|
+
return new File([existing], name, { type });
|
|
4857
|
+
}
|
|
4858
|
+
return new File([bytes], name, { type });
|
|
4859
|
+
}
|
|
4860
|
+
function sanitizeName(value, fallback) {
|
|
4861
|
+
const raw = typeof value === "string" ? value.trim() : "";
|
|
4862
|
+
const name = raw || fallback || "image.png";
|
|
4863
|
+
return name.replace(/[/\\]/g, "").slice(0, 180) || "image.png";
|
|
4864
|
+
}
|
|
4865
|
+
function normalizeType(value, name) {
|
|
4866
|
+
const raw = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
4867
|
+
if (raw === "image/jpg") return "image/jpeg";
|
|
4868
|
+
if (ALLOWED_TYPES.has(raw)) return raw;
|
|
4869
|
+
const ext = name.split(".").pop()?.toLowerCase() || "";
|
|
4870
|
+
return EXT_TO_TYPE[ext] || raw;
|
|
4871
|
+
}
|
|
4872
|
+
function asBlob(value) {
|
|
4873
|
+
if (typeof File !== "undefined" && value instanceof File) return value;
|
|
4874
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) return value;
|
|
4875
|
+
return null;
|
|
4876
|
+
}
|
|
4877
|
+
function bytesFromUnknown(value) {
|
|
4878
|
+
if (!value) return null;
|
|
4879
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
4880
|
+
if (ArrayBuffer.isView(value)) {
|
|
4881
|
+
const view = value;
|
|
4882
|
+
return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
4883
|
+
}
|
|
4884
|
+
if (Array.isArray(value)) {
|
|
4885
|
+
return Uint8Array.from(value.map((n) => Number(n) & 255));
|
|
4886
|
+
}
|
|
4887
|
+
if (typeof value === "string") {
|
|
4888
|
+
return bytesFromBase64(value);
|
|
4889
|
+
}
|
|
4890
|
+
return null;
|
|
4891
|
+
}
|
|
4892
|
+
function bytesFromBase64(value) {
|
|
4893
|
+
const comma = value.indexOf(",");
|
|
4894
|
+
const raw = value.startsWith("data:") && comma !== -1 ? value.slice(comma + 1) : value;
|
|
4895
|
+
const binary = atob(raw);
|
|
4896
|
+
const out = new Uint8Array(binary.length);
|
|
4897
|
+
for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
|
|
4898
|
+
return out;
|
|
4899
|
+
}
|
|
4804
4900
|
var VVVEB_CHANNEL = "vvveb-bridge";
|
|
4901
|
+
function frameOrigin(frame) {
|
|
4902
|
+
try {
|
|
4903
|
+
return new URL(frame?.src || "", window.location.href).origin;
|
|
4904
|
+
} catch {
|
|
4905
|
+
return window.location.origin;
|
|
4906
|
+
}
|
|
4907
|
+
}
|
|
4805
4908
|
function PlatformVisualEditorV2({
|
|
4806
4909
|
// channel kept for API compatibility; VvvebJs uses its own internal channel
|
|
4807
4910
|
embeddedGlobalKey = "__CONVERSION_EMBEDDED__",
|
|
@@ -4832,6 +4935,7 @@ function PlatformVisualEditorV2({
|
|
|
4832
4935
|
onSaveSuccess,
|
|
4833
4936
|
onSaveError,
|
|
4834
4937
|
onRequestSave,
|
|
4938
|
+
onUploadImage,
|
|
4835
4939
|
onNavigateRequested,
|
|
4836
4940
|
onDiscardDirty,
|
|
4837
4941
|
renderHeader,
|
|
@@ -4860,9 +4964,36 @@ function PlatformVisualEditorV2({
|
|
|
4860
4964
|
const sendToVvveb = useCallback((type, payload) => {
|
|
4861
4965
|
iframeRef.current?.contentWindow?.postMessage(
|
|
4862
4966
|
{ channel: VVVEB_CHANNEL, type, payload },
|
|
4863
|
-
|
|
4967
|
+
frameOrigin(iframeRef.current)
|
|
4864
4968
|
);
|
|
4865
4969
|
}, []);
|
|
4970
|
+
const sendSaveResult = useCallback(
|
|
4971
|
+
(payload, err) => {
|
|
4972
|
+
const requestId = typeof payload?.requestId === "string" ? payload.requestId : null;
|
|
4973
|
+
if (!err) {
|
|
4974
|
+
sendToVvveb("save-result", { requestId, ok: true, savedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
4977
|
+
const message = err instanceof Error ? err.message : typeof err === "string" ? err : "The save was refused";
|
|
4978
|
+
sendToVvveb("save-result", { requestId, ok: false, error: message });
|
|
4979
|
+
},
|
|
4980
|
+
[sendToVvveb]
|
|
4981
|
+
);
|
|
4982
|
+
const sendUploadImageResult = useCallback(
|
|
4983
|
+
(payload, result, err) => {
|
|
4984
|
+
const requestId = typeof payload?.requestId === "string" ? payload.requestId : null;
|
|
4985
|
+
if (!err) {
|
|
4986
|
+
sendToVvveb("upload-image-result", { requestId, ok: true, url: result?.url || "" });
|
|
4987
|
+
return;
|
|
4988
|
+
}
|
|
4989
|
+
sendToVvveb("upload-image-result", {
|
|
4990
|
+
requestId,
|
|
4991
|
+
ok: false,
|
|
4992
|
+
error: uploadImageErrorMessage(err)
|
|
4993
|
+
});
|
|
4994
|
+
},
|
|
4995
|
+
[sendToVvveb]
|
|
4996
|
+
);
|
|
4866
4997
|
const loadPayload = useMemo(
|
|
4867
4998
|
() => ({
|
|
4868
4999
|
iid: experiment?.iid,
|
|
@@ -4874,9 +5005,16 @@ function PlatformVisualEditorV2({
|
|
|
4874
5005
|
conversionProxyBaseUrl: typeof conversionProxyBaseUrl === "string" ? conversionProxyBaseUrl.trim().replace(/\/+$/, "") : "",
|
|
4875
5006
|
strictObserverFreeze: !!strictObserverFreeze,
|
|
4876
5007
|
trackingMarkers: Array.isArray(trackingMarkers) ? trackingMarkers.filter((m) => typeof m === "string" && m.trim().length > 0) : [],
|
|
4877
|
-
variations: experiment?.variations ?? []
|
|
5008
|
+
variations: experiment?.variations ?? [],
|
|
5009
|
+
// S1: this package answers every save with "save-result". A shell that
|
|
5010
|
+
// knows the field waits for the answer before it clears its working
|
|
5011
|
+
// state; an older shell ignores the field and behaves as it did before.
|
|
5012
|
+
capabilities: {
|
|
5013
|
+
saveResult: true,
|
|
5014
|
+
...onUploadImage ? { uploadImage: true } : {}
|
|
5015
|
+
}
|
|
4878
5016
|
}),
|
|
4879
|
-
[conversionProxyBaseUrl, experiment, strictObserverFreeze, trackingMarkers]
|
|
5017
|
+
[conversionProxyBaseUrl, experiment, onUploadImage, strictObserverFreeze, trackingMarkers]
|
|
4880
5018
|
);
|
|
4881
5019
|
const editorSrc = useMemo(() => {
|
|
4882
5020
|
const workerBase = normalizeProxyBaseUrl(conversionProxyBaseUrl);
|
|
@@ -4895,6 +5033,8 @@ function PlatformVisualEditorV2({
|
|
|
4895
5033
|
const handleMessage = useCallback(
|
|
4896
5034
|
async (e) => {
|
|
4897
5035
|
if (!e.data || e.data.channel !== VVVEB_CHANNEL) return;
|
|
5036
|
+
if (!iframeRef.current || e.source !== iframeRef.current.contentWindow) return;
|
|
5037
|
+
if (e.origin !== frameOrigin(iframeRef.current)) return;
|
|
4898
5038
|
const { type, payload } = e.data;
|
|
4899
5039
|
switch (type) {
|
|
4900
5040
|
case "editor-ready":
|
|
@@ -4922,8 +5062,10 @@ function PlatformVisualEditorV2({
|
|
|
4922
5062
|
try {
|
|
4923
5063
|
await onRequestSave(payload ?? {});
|
|
4924
5064
|
setDirty(false);
|
|
5065
|
+
sendSaveResult(payload, null);
|
|
4925
5066
|
onSaveSuccess?.(payload ?? {});
|
|
4926
5067
|
} catch (err) {
|
|
5068
|
+
sendSaveResult(payload, err);
|
|
4927
5069
|
onSaveError?.(err);
|
|
4928
5070
|
}
|
|
4929
5071
|
break;
|
|
@@ -4932,13 +5074,35 @@ function PlatformVisualEditorV2({
|
|
|
4932
5074
|
try {
|
|
4933
5075
|
await onRequestSave(payload ?? {});
|
|
4934
5076
|
setDirty(false);
|
|
5077
|
+
sendSaveResult(payload, null);
|
|
4935
5078
|
onSaveSuccess?.(payload ?? {});
|
|
4936
5079
|
if (payload?.hash) onNavigateRequested?.(payload.hash);
|
|
4937
5080
|
} catch (err) {
|
|
5081
|
+
sendSaveResult(payload, err);
|
|
4938
5082
|
onSaveError?.(err);
|
|
4939
5083
|
}
|
|
4940
5084
|
break;
|
|
5085
|
+
case "upload-image":
|
|
5086
|
+
if (typeof e.stopImmediatePropagation === "function") e.stopImmediatePropagation();
|
|
5087
|
+
if (!onUploadImage) {
|
|
5088
|
+
sendUploadImageResult(payload, null, "Image upload is not available");
|
|
5089
|
+
return;
|
|
5090
|
+
}
|
|
5091
|
+
try {
|
|
5092
|
+
const file = fileFromUploadImagePayload(payload);
|
|
5093
|
+
const result = await onUploadImage(file);
|
|
5094
|
+
const url = typeof result?.url === "string" ? result.url.trim() : "";
|
|
5095
|
+
if (!url) throw new Error("The upload did not return a URL");
|
|
5096
|
+
sendUploadImageResult(payload, { url }, null);
|
|
5097
|
+
} catch (err) {
|
|
5098
|
+
sendUploadImageResult(payload, null, err);
|
|
5099
|
+
}
|
|
5100
|
+
break;
|
|
4941
5101
|
case "close-editor":
|
|
5102
|
+
if (payload?.discard === true) {
|
|
5103
|
+
onClose?.();
|
|
5104
|
+
return;
|
|
5105
|
+
}
|
|
4942
5106
|
if (!dirtyRef.current) {
|
|
4943
5107
|
onClose?.();
|
|
4944
5108
|
return;
|
|
@@ -4959,12 +5123,15 @@ function PlatformVisualEditorV2({
|
|
|
4959
5123
|
onEditorReady,
|
|
4960
5124
|
onEditorUrlChanged,
|
|
4961
5125
|
onRequestSave,
|
|
5126
|
+
onUploadImage,
|
|
4962
5127
|
onSaveSuccess,
|
|
4963
5128
|
onSaveError,
|
|
4964
5129
|
onNavigateRequested,
|
|
4965
5130
|
onClose,
|
|
4966
5131
|
onDiscardDirty,
|
|
4967
|
-
sendToVvveb
|
|
5132
|
+
sendToVvveb,
|
|
5133
|
+
sendSaveResult,
|
|
5134
|
+
sendUploadImageResult
|
|
4968
5135
|
]
|
|
4969
5136
|
);
|
|
4970
5137
|
useLayoutEffect(() => {
|