@duffcloudservices/cms 0.11.0 → 0.12.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.
@@ -56,6 +56,46 @@ 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
+ /** Clear all pending image overrides (test isolation). */
98
+ declare function clearPendingImageOverrides(): void;
59
99
  declare function initEditorBridge(): void;
60
100
 
61
- export { type EditorReadyPayload, type SectionInfo, initEditorBridge };
101
+ export { type EditorReadyPayload, type SectionInfo, applyImageOverride, applyPendingImageOverrides, clearPendingImageOverrides, initEditorBridge, recordPendingImageOverride, 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) {
@@ -663,6 +664,61 @@ function createImageEditIcon(label) {
663
664
  function getManagedImageKey(img) {
664
665
  return img.dataset.dcsImageKey || img.closest("[data-dcs-image-key]")?.dataset.dcsImageKey || null;
665
666
  }
667
+ var pendingImageOverrides = /* @__PURE__ */ new Map();
668
+ function cssEscapeAttributeValue(value) {
669
+ const cssGlobal = globalThis.CSS;
670
+ if (typeof cssGlobal?.escape === "function") return cssGlobal.escape(value);
671
+ return value.replace(/[\\"]/g, "\\$&");
672
+ }
673
+ function applyImageOverride(key, src, alt) {
674
+ if (!key) return 0;
675
+ const escapedKey = cssEscapeAttributeValue(key);
676
+ const targets = /* @__PURE__ */ new Set();
677
+ document.querySelectorAll(`img[data-dcs-image-key="${escapedKey}"]`).forEach((img) => targets.add(img));
678
+ document.querySelectorAll(`[data-dcs-image-key="${escapedKey}"]`).forEach((el) => {
679
+ if (el instanceof HTMLImageElement) {
680
+ targets.add(el);
681
+ } else {
682
+ el.querySelectorAll("img").forEach((img) => targets.add(img));
683
+ }
684
+ });
685
+ targets.forEach((img) => {
686
+ img.closest("picture")?.querySelectorAll("source").forEach((s) => s.remove());
687
+ img.removeAttribute("srcset");
688
+ img.src = src;
689
+ img.dataset.dcsImageUrl = src;
690
+ if (alt != null) {
691
+ img.alt = alt;
692
+ img.dataset.dcsImageAlt = alt;
693
+ }
694
+ });
695
+ return targets.size;
696
+ }
697
+ function setImageOverride(key, src, alt) {
698
+ pendingImageOverrides.set(key, { src, alt });
699
+ applyImageOverride(key, src, alt);
700
+ scheduleRediscovery();
701
+ }
702
+ function applyPendingImageOverrides() {
703
+ for (const [key, override] of pendingImageOverrides) {
704
+ applyImageOverride(key, override.src, override.alt);
705
+ }
706
+ }
707
+ function recordPendingImageOverride(key, src, alt) {
708
+ pendingImageOverrides.set(key, { src, alt });
709
+ }
710
+ function revertImageOverrideIfImageKey(key, value) {
711
+ if (!key) return false;
712
+ const isPending = pendingImageOverrides.has(key);
713
+ const isImageElement = !isPending && !!document.querySelector(`[data-dcs-image-key="${cssEscapeAttributeValue(key)}"]`);
714
+ if (!isPending && !isImageElement) return false;
715
+ pendingImageOverrides.delete(key);
716
+ if (value) applyImageOverride(key, value);
717
+ return true;
718
+ }
719
+ function clearPendingImageOverrides() {
720
+ pendingImageOverrides.clear();
721
+ }
666
722
  function showImageEditIcons(images) {
667
723
  if (!editingEnabled) return;
668
724
  if (imageEditIconTargets.length === images.length && images.every((img, i) => imageEditIconTargets[i] === img)) return;
@@ -1027,7 +1083,9 @@ function handleMessage(event) {
1027
1083
  case "dcs:update-text":
1028
1084
  if (data && typeof data === "object" && "key" in data && "value" in data) {
1029
1085
  const d = data;
1030
- updateTextInPlace(d.key, d.value);
1086
+ if (!revertImageOverrideIfImageKey(d.key, d.value)) {
1087
+ updateTextInPlace(d.key, d.value);
1088
+ }
1031
1089
  }
1032
1090
  break;
1033
1091
  case "dcs:set-mode":
@@ -1066,6 +1124,14 @@ function handleMessage(event) {
1066
1124
  }
1067
1125
  }
1068
1126
  break;
1127
+ case "dcs:image-set":
1128
+ if (data && typeof data === "object" && "key" in data && "src" in data) {
1129
+ const d = data;
1130
+ if (typeof d.key === "string" && typeof d.src === "string") {
1131
+ setImageOverride(d.key, d.src, d.alt ?? null);
1132
+ }
1133
+ }
1134
+ break;
1069
1135
  case "dcs:update-reviews":
1070
1136
  if (data && typeof data === "object" && "key" in data) {
1071
1137
  const d = data;
@@ -1182,6 +1248,6 @@ function initEditorBridge() {
1182
1248
  observeSectionLayout();
1183
1249
  }
1184
1250
 
1185
- export { initEditorBridge };
1251
+ export { applyImageOverride, applyPendingImageOverrides, clearPendingImageOverrides, initEditorBridge, recordPendingImageOverride, revertImageOverrideIfImageKey };
1186
1252
  //# sourceMappingURL=editorBridge.js.map
1187
1253
  //# sourceMappingURL=editorBridge.js.map