@duffcloudservices/cms 0.11.0 → 0.13.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/README.md +244 -8
- package/dist/chunk-A5F4C72F.js +500 -0
- package/dist/chunk-A5F4C72F.js.map +1 -0
- package/dist/{chunk-F3EIWEZD.js → chunk-HVSF23P7.js} +971 -73
- package/dist/chunk-HVSF23P7.js.map +1 -0
- package/dist/editor/editorBridge.d.ts +53 -1
- package/dist/editor/editorBridge.js +141 -5
- package/dist/editor/editorBridge.js.map +1 -1
- package/dist/headHonesty-OzxvLuwd.d.ts +222 -0
- package/dist/index.d.ts +369 -22
- package/dist/index.js +424 -22
- package/dist/index.js.map +1 -1
- package/dist/installSeoHead-kWQwObez.d.ts +627 -0
- package/dist/plugins/index.d.ts +173 -6
- package/dist/plugins/index.js +628 -54
- package/dist/plugins/index.js.map +1 -1
- package/dist/seo/index.d.ts +763 -4
- package/dist/seo/index.js +2 -2
- package/dist/{vitepressTransform-DfmABXmK.d.ts → vitepressTransform-JG_zlaux.d.ts} +99 -6
- package/package.json +26 -16
- package/src/components/DcsCallButton.test.ts +58 -0
- package/src/components/DcsCallButton.vue +19 -4
- package/src/components/DcsReviewShowcase.vue +5 -1
- package/src/components/LiteMediaEmbed.vue +3 -3
- package/src/components/ManagedImage.test.ts +94 -0
- package/src/components/ManagedImage.vue +58 -6
- package/src/components/PreviewRibbon.vue +4 -1
- package/src/composables/useConversionTracking.test.ts +492 -0
- package/src/composables/useConversionTracking.ts +770 -0
- package/src/composables/useReleaseNotes.ts +7 -1
- package/src/composables/useResponsiveImage.ts +6 -0
- package/src/composables/useSEO.applyHead.test.ts +150 -0
- package/src/composables/useSEO.ts +63 -17
- package/src/composables/useSiteVersion.ts +4 -1
- package/src/composables/useSiteVisitorSession.test.ts +56 -0
- package/src/composables/useSiteVisitorSession.ts +39 -3
- package/src/composables/useTextContent.ts +9 -1
- package/dist/chunk-DAYLLSEE.js +0 -3
- package/dist/chunk-DAYLLSEE.js.map +0 -1
- package/dist/chunk-F3EIWEZD.js.map +0 -1
- package/dist/spliceHeadHtml-CsBEucGy.d.ts +0 -254
|
@@ -56,6 +56,58 @@ interface EditorReadyPayload {
|
|
|
56
56
|
/** Keys of all [data-dcs-reviews] elements found on the page */
|
|
57
57
|
reviewKeys: string[];
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Swap the live `<img>`(s) carrying a managed image key to a new source.
|
|
61
|
+
*
|
|
62
|
+
* Mirrors the self-heal invariant: a `<picture>` does NOT re-resolve after its
|
|
63
|
+
* `<source>` set changes, so the stale `<source>` siblings are removed and the
|
|
64
|
+
* `srcset` on the `<img>` cleared — otherwise the browser keeps serving the old
|
|
65
|
+
* variant. `data-dcs-image-url`/`-alt` are updated too so a subsequent
|
|
66
|
+
* right-click reports the new image, not the replaced one.
|
|
67
|
+
*
|
|
68
|
+
* @returns the number of elements updated (0 when the key isn't on the page).
|
|
69
|
+
*/
|
|
70
|
+
declare function applyImageOverride(key: string, src: string, alt?: string | null): number;
|
|
71
|
+
/**
|
|
72
|
+
* Re-apply all pending image overrides. Called after a re-render (HMR / SPA nav)
|
|
73
|
+
* so a hot reload doesn't revert a not-yet-saved swap.
|
|
74
|
+
*/
|
|
75
|
+
declare function applyPendingImageOverrides(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Record a pending image override (test/utility seam for the replay path).
|
|
78
|
+
*/
|
|
79
|
+
declare function recordPendingImageOverride(key: string, src: string, alt?: string | null): void;
|
|
80
|
+
/**
|
|
81
|
+
* Handle a `dcs:update-text` that actually targets a MANAGED IMAGE key.
|
|
82
|
+
*
|
|
83
|
+
* The portal's discard/undo paths (discardTextChanges / discardSingleTextChange
|
|
84
|
+
* in visualEditor.ts) revert EVERY staged key — including image keys — via the
|
|
85
|
+
* text revert callback, which sends `dcs:update-text`. `updateTextInPlace` only
|
|
86
|
+
* touches `data-text-key` elements, a no-op for image keys (which live on
|
|
87
|
+
* `data-dcs-image-key`), while `pendingImageOverrides` would keep re-applying
|
|
88
|
+
* the discarded src on every re-render — the discarded swap stayed sticky until
|
|
89
|
+
* a manual refresh. So: if the key has a pending image override (or a managed
|
|
90
|
+
* image element on the page), drop the sticky override and swap the live
|
|
91
|
+
* `<img>` back to the reverted URL. Covers discard-all, discard-single, undo,
|
|
92
|
+
* and reload-drops-staged-swap in one place.
|
|
93
|
+
*
|
|
94
|
+
* @returns true when the key was handled as an image revert (skip text handling).
|
|
95
|
+
*/
|
|
96
|
+
declare function revertImageOverrideIfImageKey(key: string, value: string): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Handle a `dcs:update-text` that targets a MANAGED IMAGE **ALT** key
|
|
99
|
+
* (`data-dcs-image-alt-key`). The portal stages an alt change alongside the
|
|
100
|
+
* src change on "Replace without AI"; on discard/undo it reverts each staged
|
|
101
|
+
* key via `dcs:update-text`. Alt keys are attributes, not text nodes, so
|
|
102
|
+
* `updateTextInPlace` would be a silent no-op — instead restore the `<img>`
|
|
103
|
+
* alt and refresh any pending image override (keyed by the SRC key) so a
|
|
104
|
+
* re-render replay does not resurrect the discarded alt.
|
|
105
|
+
*
|
|
106
|
+
* @returns true when the key was handled as an image-alt revert.
|
|
107
|
+
*/
|
|
108
|
+
declare function revertImageAltOverrideIfAltKey(key: string, value: string): boolean;
|
|
109
|
+
/** Clear all pending image overrides (test isolation). */
|
|
110
|
+
declare function clearPendingImageOverrides(): void;
|
|
59
111
|
declare function initEditorBridge(): void;
|
|
60
112
|
|
|
61
|
-
export { type EditorReadyPayload, type SectionInfo, initEditorBridge };
|
|
113
|
+
export { type EditorReadyPayload, type SectionInfo, applyImageOverride, applyPendingImageOverrides, clearPendingImageOverrides, initEditorBridge, recordPendingImageOverride, revertImageAltOverrideIfAltKey, revertImageOverrideIfImageKey };
|
|
@@ -193,6 +193,7 @@ function rediscoverAndNotify() {
|
|
|
193
193
|
hasReviews: reviewKeys.length > 0,
|
|
194
194
|
reviewKeys
|
|
195
195
|
});
|
|
196
|
+
applyPendingImageOverrides();
|
|
196
197
|
if (editorActive) {
|
|
197
198
|
applyEditorToElements();
|
|
198
199
|
if (sectionResizeObserver) {
|
|
@@ -217,6 +218,7 @@ function applyEditorToElements() {
|
|
|
217
218
|
htmlEl.addEventListener("dblclick", handleTextKeyDblClick);
|
|
218
219
|
htmlEl.addEventListener("blur", handleTextKeyBlur);
|
|
219
220
|
htmlEl.addEventListener("keydown", handleTextKeyKeydown);
|
|
221
|
+
htmlEl.addEventListener("paste", handleTextKeyPaste);
|
|
220
222
|
htmlEl.addEventListener("mouseenter", () => {
|
|
221
223
|
if (activeEditElement) return;
|
|
222
224
|
const key = getTextKey(htmlEl);
|
|
@@ -387,12 +389,21 @@ function handleTextKeyDblClick(e) {
|
|
|
387
389
|
postToParent("dcs:text-key-dblclick", { key, sectionId });
|
|
388
390
|
startInlineEdit(el, key, sectionId);
|
|
389
391
|
}
|
|
392
|
+
function supportsPlaintextOnly() {
|
|
393
|
+
try {
|
|
394
|
+
const probe = document.createElement("div");
|
|
395
|
+
probe.contentEditable = "plaintext-only";
|
|
396
|
+
return probe.getAttribute("contenteditable") === "plaintext-only";
|
|
397
|
+
} catch {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
390
401
|
function startInlineEdit(el, key, sectionId) {
|
|
391
402
|
if (activeEditElement && activeEditElement !== el) {
|
|
392
403
|
finishEdit(activeEditElement);
|
|
393
404
|
}
|
|
394
405
|
originalTextValues.set(el, el.innerText.trim());
|
|
395
|
-
el.setAttribute("contenteditable", "true");
|
|
406
|
+
el.setAttribute("contenteditable", supportsPlaintextOnly() ? "plaintext-only" : "true");
|
|
396
407
|
el.classList.add("dcs-editing");
|
|
397
408
|
el.focus();
|
|
398
409
|
activeEditElement = el;
|
|
@@ -414,9 +425,44 @@ function handleTextKeyKeydown(e) {
|
|
|
414
425
|
}
|
|
415
426
|
if (e.key === "Escape") {
|
|
416
427
|
e.preventDefault();
|
|
417
|
-
e.currentTarget
|
|
428
|
+
cancelInlineEdit(e.currentTarget);
|
|
418
429
|
}
|
|
419
430
|
}
|
|
431
|
+
function handleTextKeyPaste(e) {
|
|
432
|
+
const el = e.currentTarget;
|
|
433
|
+
if (el !== activeEditElement) return;
|
|
434
|
+
if (el.getAttribute("contenteditable") === "plaintext-only") return;
|
|
435
|
+
const text = e.clipboardData?.getData("text/plain") ?? "";
|
|
436
|
+
e.preventDefault();
|
|
437
|
+
insertPlainTextAtCaret(el, text);
|
|
438
|
+
}
|
|
439
|
+
function insertPlainTextAtCaret(el, text) {
|
|
440
|
+
const selection = getSelection();
|
|
441
|
+
if (!selection || selection.rangeCount === 0) {
|
|
442
|
+
el.appendChild(document.createTextNode(text));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const range = selection.getRangeAt(0);
|
|
446
|
+
if (!el.contains(range.commonAncestorContainer)) return;
|
|
447
|
+
range.deleteContents();
|
|
448
|
+
if (text) {
|
|
449
|
+
const node = document.createTextNode(text);
|
|
450
|
+
range.insertNode(node);
|
|
451
|
+
range.setStartAfter(node);
|
|
452
|
+
}
|
|
453
|
+
range.collapse(true);
|
|
454
|
+
selection.removeAllRanges();
|
|
455
|
+
selection.addRange(range);
|
|
456
|
+
}
|
|
457
|
+
function cancelInlineEdit(el) {
|
|
458
|
+
if (!el.hasAttribute("contenteditable")) return;
|
|
459
|
+
const original = originalTextValues.get(el);
|
|
460
|
+
if (original !== void 0) {
|
|
461
|
+
el.innerText = original;
|
|
462
|
+
}
|
|
463
|
+
el.blur();
|
|
464
|
+
finishEdit(el);
|
|
465
|
+
}
|
|
420
466
|
function finishEdit(el) {
|
|
421
467
|
if (!el.hasAttribute("contenteditable")) return;
|
|
422
468
|
el.removeAttribute("contenteditable");
|
|
@@ -663,6 +709,83 @@ function createImageEditIcon(label) {
|
|
|
663
709
|
function getManagedImageKey(img) {
|
|
664
710
|
return img.dataset.dcsImageKey || img.closest("[data-dcs-image-key]")?.dataset.dcsImageKey || null;
|
|
665
711
|
}
|
|
712
|
+
function getManagedImageAltKey(img) {
|
|
713
|
+
return img.dataset.dcsImageAltKey || img.closest("[data-dcs-image-alt-key]")?.dataset.dcsImageAltKey || null;
|
|
714
|
+
}
|
|
715
|
+
var pendingImageOverrides = /* @__PURE__ */ new Map();
|
|
716
|
+
function cssEscapeAttributeValue(value) {
|
|
717
|
+
const cssGlobal = globalThis.CSS;
|
|
718
|
+
if (typeof cssGlobal?.escape === "function") return cssGlobal.escape(value);
|
|
719
|
+
return value.replace(/[\\"]/g, "\\$&");
|
|
720
|
+
}
|
|
721
|
+
function applyImageOverride(key, src, alt) {
|
|
722
|
+
if (!key) return 0;
|
|
723
|
+
const escapedKey = cssEscapeAttributeValue(key);
|
|
724
|
+
const targets = /* @__PURE__ */ new Set();
|
|
725
|
+
document.querySelectorAll(`img[data-dcs-image-key="${escapedKey}"]`).forEach((img) => targets.add(img));
|
|
726
|
+
document.querySelectorAll(`[data-dcs-image-key="${escapedKey}"]`).forEach((el) => {
|
|
727
|
+
if (el instanceof HTMLImageElement) {
|
|
728
|
+
targets.add(el);
|
|
729
|
+
} else {
|
|
730
|
+
el.querySelectorAll("img").forEach((img) => targets.add(img));
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
targets.forEach((img) => {
|
|
734
|
+
img.closest("picture")?.querySelectorAll("source").forEach((s) => s.remove());
|
|
735
|
+
img.removeAttribute("srcset");
|
|
736
|
+
img.src = src;
|
|
737
|
+
img.dataset.dcsImageUrl = src;
|
|
738
|
+
if (alt != null) {
|
|
739
|
+
img.alt = alt;
|
|
740
|
+
img.dataset.dcsImageAlt = alt;
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
return targets.size;
|
|
744
|
+
}
|
|
745
|
+
function setImageOverride(key, src, alt) {
|
|
746
|
+
pendingImageOverrides.set(key, { src, alt });
|
|
747
|
+
applyImageOverride(key, src, alt);
|
|
748
|
+
scheduleRediscovery();
|
|
749
|
+
}
|
|
750
|
+
function applyPendingImageOverrides() {
|
|
751
|
+
for (const [key, override] of pendingImageOverrides) {
|
|
752
|
+
applyImageOverride(key, override.src, override.alt);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
function recordPendingImageOverride(key, src, alt) {
|
|
756
|
+
pendingImageOverrides.set(key, { src, alt });
|
|
757
|
+
}
|
|
758
|
+
function revertImageOverrideIfImageKey(key, value) {
|
|
759
|
+
if (!key) return false;
|
|
760
|
+
const isPending = pendingImageOverrides.has(key);
|
|
761
|
+
const isImageElement = !isPending && !!document.querySelector(`[data-dcs-image-key="${cssEscapeAttributeValue(key)}"]`);
|
|
762
|
+
if (!isPending && !isImageElement) return false;
|
|
763
|
+
pendingImageOverrides.delete(key);
|
|
764
|
+
if (value) applyImageOverride(key, value);
|
|
765
|
+
return true;
|
|
766
|
+
}
|
|
767
|
+
function revertImageAltOverrideIfAltKey(key, value) {
|
|
768
|
+
if (!key) return false;
|
|
769
|
+
const escapedKey = cssEscapeAttributeValue(key);
|
|
770
|
+
const hosts = document.querySelectorAll(`[data-dcs-image-alt-key="${escapedKey}"]`);
|
|
771
|
+
if (hosts.length === 0) return false;
|
|
772
|
+
hosts.forEach((host) => {
|
|
773
|
+
const imgs = host instanceof HTMLImageElement ? [host] : Array.from(host.querySelectorAll("img"));
|
|
774
|
+
imgs.forEach((img) => {
|
|
775
|
+
img.alt = value;
|
|
776
|
+
img.dataset.dcsImageAlt = value;
|
|
777
|
+
});
|
|
778
|
+
const srcKey = host.dataset.dcsImageKey || host.closest("[data-dcs-image-key]")?.dataset.dcsImageKey;
|
|
779
|
+
if (srcKey) {
|
|
780
|
+
const pending = pendingImageOverrides.get(srcKey);
|
|
781
|
+
if (pending) pendingImageOverrides.set(srcKey, { ...pending, alt: value });
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
function clearPendingImageOverrides() {
|
|
787
|
+
pendingImageOverrides.clear();
|
|
788
|
+
}
|
|
666
789
|
function showImageEditIcons(images) {
|
|
667
790
|
if (!editingEnabled) return;
|
|
668
791
|
if (imageEditIconTargets.length === images.length && images.every((img, i) => imageEditIconTargets[i] === img)) return;
|
|
@@ -689,6 +812,7 @@ function showImageEditIcons(images) {
|
|
|
689
812
|
imageWidth: img.naturalWidth,
|
|
690
813
|
imageHeight: img.naturalHeight,
|
|
691
814
|
imageKey: getManagedImageKey(img),
|
|
815
|
+
imageAltKey: getManagedImageAltKey(img),
|
|
692
816
|
sectionId: sectionEl?.dataset.section ?? null,
|
|
693
817
|
sectionLabel: sectionEl?.dataset.sectionLabel ?? null
|
|
694
818
|
});
|
|
@@ -1027,7 +1151,9 @@ function handleMessage(event) {
|
|
|
1027
1151
|
case "dcs:update-text":
|
|
1028
1152
|
if (data && typeof data === "object" && "key" in data && "value" in data) {
|
|
1029
1153
|
const d = data;
|
|
1030
|
-
|
|
1154
|
+
if (!revertImageOverrideIfImageKey(d.key, d.value) && !revertImageAltOverrideIfAltKey(d.key, d.value)) {
|
|
1155
|
+
updateTextInPlace(d.key, d.value);
|
|
1156
|
+
}
|
|
1031
1157
|
}
|
|
1032
1158
|
break;
|
|
1033
1159
|
case "dcs:set-mode":
|
|
@@ -1066,6 +1192,14 @@ function handleMessage(event) {
|
|
|
1066
1192
|
}
|
|
1067
1193
|
}
|
|
1068
1194
|
break;
|
|
1195
|
+
case "dcs:image-set":
|
|
1196
|
+
if (data && typeof data === "object" && "key" in data && "src" in data) {
|
|
1197
|
+
const d = data;
|
|
1198
|
+
if (typeof d.key === "string" && typeof d.src === "string") {
|
|
1199
|
+
setImageOverride(d.key, d.src, d.alt ?? null);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
break;
|
|
1069
1203
|
case "dcs:update-reviews":
|
|
1070
1204
|
if (data && typeof data === "object" && "key" in data) {
|
|
1071
1205
|
const d = data;
|
|
@@ -1135,13 +1269,15 @@ function initEditorBridge() {
|
|
|
1135
1269
|
imageWidth: topImage?.naturalWidth ?? null,
|
|
1136
1270
|
imageHeight: topImage?.naturalHeight ?? null,
|
|
1137
1271
|
imageKey: topImage ? getManagedImageKey(topImage) : null,
|
|
1272
|
+
imageAltKey: topImage ? getManagedImageAltKey(topImage) : null,
|
|
1138
1273
|
// All images at click point (for overlapping image scenarios like before/after sliders)
|
|
1139
1274
|
images: allImages.length > 1 ? allImages.map((img) => ({
|
|
1140
1275
|
imageUrl: img.currentSrc ?? img.src,
|
|
1141
1276
|
imageAlt: img.alt ?? null,
|
|
1142
1277
|
imageWidth: img.naturalWidth,
|
|
1143
1278
|
imageHeight: img.naturalHeight,
|
|
1144
|
-
imageKey: getManagedImageKey(img)
|
|
1279
|
+
imageKey: getManagedImageKey(img),
|
|
1280
|
+
imageAltKey: getManagedImageAltKey(img)
|
|
1145
1281
|
})) : null
|
|
1146
1282
|
});
|
|
1147
1283
|
}, true);
|
|
@@ -1182,6 +1318,6 @@ function initEditorBridge() {
|
|
|
1182
1318
|
observeSectionLayout();
|
|
1183
1319
|
}
|
|
1184
1320
|
|
|
1185
|
-
export { initEditorBridge };
|
|
1321
|
+
export { applyImageOverride, applyPendingImageOverrides, clearPendingImageOverrides, initEditorBridge, recordPendingImageOverride, revertImageAltOverrideIfAltKey, revertImageOverrideIfImageKey };
|
|
1186
1322
|
//# sourceMappingURL=editorBridge.js.map
|
|
1187
1323
|
//# sourceMappingURL=editorBridge.js.map
|