@ohhwells/bridge 0.1.36-next.48 → 0.1.36-next.49

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/index.js CHANGED
@@ -5866,6 +5866,9 @@ function collectEditableNodes() {
5866
5866
  const url = raw.replace(/^url\(['"]?/, "").replace(/['"]?\)$/, "");
5867
5867
  return { key: el.dataset.ohwKey ?? "", type: "bg-image", text: url };
5868
5868
  }
5869
+ if (el.dataset.ohwEditable === "video") {
5870
+ return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
5871
+ }
5869
5872
  if (el.dataset.ohwEditable === "link") {
5870
5873
  return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
5871
5874
  }
@@ -5880,8 +5883,62 @@ function collectEditableNodes() {
5880
5883
  if (!key) return;
5881
5884
  nodes.push({ key, type: "link", text: getLinkHref(el) });
5882
5885
  });
5886
+ document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
5887
+ const key = el.dataset.ohwKey ?? "";
5888
+ const video = getVideoEl(el);
5889
+ if (!key || !video) return;
5890
+ nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
5891
+ nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
5892
+ });
5883
5893
  return nodes;
5884
5894
  }
5895
+ function isMediaEditable(el) {
5896
+ const t = el.dataset.ohwEditable;
5897
+ return t === "image" || t === "bg-image" || t === "video";
5898
+ }
5899
+ var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
5900
+ var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"])';
5901
+ function getVideoEl(el) {
5902
+ return el instanceof HTMLVideoElement ? el : el.querySelector("video");
5903
+ }
5904
+ var VIDEO_AUTOPLAY_SUFFIX = "__ohw_autoplay";
5905
+ var VIDEO_MUTED_SUFFIX = "__ohw_muted";
5906
+ function isBackgroundVideo(video) {
5907
+ return video.autoplay && video.muted;
5908
+ }
5909
+ function syncVideoPlayback(video) {
5910
+ const background = isBackgroundVideo(video);
5911
+ video.controls = !background;
5912
+ if (video.autoplay) void video.play().catch(() => {
5913
+ });
5914
+ else video.pause();
5915
+ }
5916
+ function applyVideoSrc(video, url) {
5917
+ const { autoplay, muted } = video;
5918
+ video.src = url;
5919
+ video.loop = true;
5920
+ video.playsInline = true;
5921
+ video.autoplay = autoplay;
5922
+ video.muted = muted;
5923
+ video.load();
5924
+ syncVideoPlayback(video);
5925
+ }
5926
+ function applyVideoSettingNode(key, val) {
5927
+ const isAutoplay = key.endsWith(VIDEO_AUTOPLAY_SUFFIX);
5928
+ const isMuted = key.endsWith(VIDEO_MUTED_SUFFIX);
5929
+ if (!isAutoplay && !isMuted) return false;
5930
+ const suffixLen = (isAutoplay ? VIDEO_AUTOPLAY_SUFFIX : VIDEO_MUTED_SUFFIX).length;
5931
+ const baseKey = key.slice(0, key.length - suffixLen);
5932
+ const on = val === "true";
5933
+ document.querySelectorAll(`[data-ohw-key="${baseKey}"]`).forEach((el) => {
5934
+ const video = getVideoEl(el);
5935
+ if (!video) return;
5936
+ if (isAutoplay) video.autoplay = on;
5937
+ else video.muted = on;
5938
+ syncVideoPlayback(video);
5939
+ });
5940
+ return true;
5941
+ }
5885
5942
  function applyLinkByKey(key, val) {
5886
5943
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
5887
5944
  if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
@@ -6870,6 +6927,7 @@ function OhhwellsBridge() {
6870
6927
  const imageLoads = [];
6871
6928
  for (const [key, val] of Object.entries(content)) {
6872
6929
  if (key === "__ohw_sections") continue;
6930
+ if (applyVideoSettingNode(key, val)) continue;
6873
6931
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6874
6932
  if (el.dataset.ohwEditable === "image") {
6875
6933
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6883,6 +6941,15 @@ function OhhwellsBridge() {
6883
6941
  } else if (el.dataset.ohwEditable === "bg-image") {
6884
6942
  const next = `url('${val}')`;
6885
6943
  if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
6944
+ } else if (el.dataset.ohwEditable === "video") {
6945
+ const video = getVideoEl(el);
6946
+ if (video && video.src !== val) {
6947
+ applyVideoSrc(video, val);
6948
+ imageLoads.push(new Promise((resolve) => {
6949
+ video.onloadeddata = () => resolve();
6950
+ video.onerror = () => resolve();
6951
+ }));
6952
+ }
6886
6953
  } else if (el.dataset.ohwEditable === "link") {
6887
6954
  applyLinkHref(el, val);
6888
6955
  } else if (el.innerHTML !== val) {
@@ -6931,6 +6998,7 @@ function OhhwellsBridge() {
6931
6998
  try {
6932
6999
  for (const [key, val] of Object.entries(content)) {
6933
7000
  if (key === "__ohw_sections") continue;
7001
+ if (applyVideoSettingNode(key, val)) continue;
6934
7002
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6935
7003
  if (el.dataset.ohwEditable === "image") {
6936
7004
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6938,6 +7006,9 @@ function OhhwellsBridge() {
6938
7006
  } else if (el.dataset.ohwEditable === "bg-image") {
6939
7007
  const next = `url('${val}')`;
6940
7008
  if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
7009
+ } else if (el.dataset.ohwEditable === "video") {
7010
+ const video = getVideoEl(el);
7011
+ if (video && video.src !== val) applyVideoSrc(video, val);
6941
7012
  } else if (el.dataset.ohwEditable === "link") {
6942
7013
  applyLinkHref(el, val);
6943
7014
  } else if (el.innerHTML !== val) {
@@ -7041,8 +7112,9 @@ function OhhwellsBridge() {
7041
7112
  [data-ohw-editable] {
7042
7113
  display: block;
7043
7114
  }
7044
- [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]) { cursor: text !important; }
7115
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
7045
7116
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
7117
+ [data-ohw-editable="video"], [data-ohw-editable="video"] *,
7046
7118
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
7047
7119
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
7048
7120
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
@@ -7109,7 +7181,7 @@ function OhhwellsBridge() {
7109
7181
  });
7110
7182
  return;
7111
7183
  }
7112
- if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
7184
+ if (isMediaEditable(editable)) {
7113
7185
  e.preventDefault();
7114
7186
  e.stopPropagation();
7115
7187
  postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
@@ -7212,7 +7284,7 @@ function OhhwellsBridge() {
7212
7284
  if (!editable) return;
7213
7285
  const selected = selectedElRef.current;
7214
7286
  if (selected && (selected === editable || selected.contains(editable))) return;
7215
- if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
7287
+ if (!isMediaEditable(editable) && !editable.hasAttribute("contenteditable")) {
7216
7288
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
7217
7289
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
7218
7290
  clearHrefKeyHover(hoverTarget);
@@ -7240,7 +7312,7 @@ function OhhwellsBridge() {
7240
7312
  if (!editable) return;
7241
7313
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
7242
7314
  if (related?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
7243
- if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image") {
7315
+ if (!isMediaEditable(editable)) {
7244
7316
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
7245
7317
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
7246
7318
  if (!related?.closest("[data-ohw-href-key]")) {
@@ -7290,11 +7362,16 @@ function OhhwellsBridge() {
7290
7362
  const r2 = getVisibleRect(imgEl);
7291
7363
  const hasTextOverlap = forceTextOverlap ?? false;
7292
7364
  hoveredImageHasTextOverlapRef.current = hasTextOverlap;
7365
+ const video = imgEl.dataset.ohwEditable === "video" ? getVideoEl(imgEl) : null;
7293
7366
  postToParentRef.current({
7294
7367
  type: "ow:image-hover",
7295
7368
  key: imgEl.dataset.ohwKey ?? "",
7369
+ // Lets the parent pick the file-picker `accept`, overlay label/icon and drop
7370
+ // mime-check without a parallel ow:video-* message channel.
7371
+ elementType: imgEl.dataset.ohwEditable ?? "image",
7296
7372
  rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
7297
7373
  hasTextOverlap,
7374
+ ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {},
7298
7375
  ...isDragOver ? { isDragOver: true } : {}
7299
7376
  });
7300
7377
  const track = imgEl.closest("[data-ohw-hover-pause]");
@@ -7302,7 +7379,7 @@ function OhhwellsBridge() {
7302
7379
  };
7303
7380
  const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
7304
7381
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7305
- const images = Array.from(document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]'));
7382
+ const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
7306
7383
  const matches = [];
7307
7384
  for (let i = images.length - 1; i >= 0; i--) {
7308
7385
  const el = images[i];
@@ -7318,7 +7395,9 @@ function OhhwellsBridge() {
7318
7395
  if (x >= r2.left && x <= r2.left + r2.width && y >= r2.top && y <= r2.top + r2.height) matches.push(el);
7319
7396
  }
7320
7397
  if (matches.length === 0) return null;
7321
- const imageTypeMatches = matches.filter((el) => el.dataset.ohwEditable === "image");
7398
+ const imageTypeMatches = matches.filter(
7399
+ (el) => el.dataset.ohwEditable === "image" || el.dataset.ohwEditable === "video"
7400
+ );
7322
7401
  const candidatePool = imageTypeMatches.length > 0 ? imageTypeMatches : matches;
7323
7402
  const smallest = candidatePool.reduce((best, el) => {
7324
7403
  const br = getVisibleRect(best);
@@ -7396,7 +7475,7 @@ function OhhwellsBridge() {
7396
7475
  }
7397
7476
  const isStateCardImage = !!imgEl.closest("[data-ohw-editable-state]");
7398
7477
  const textEditable = Array.from(
7399
- document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
7478
+ document.querySelectorAll(NON_MEDIA_SELECTOR)
7400
7479
  ).find((el) => {
7401
7480
  const er = el.getBoundingClientRect();
7402
7481
  return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
@@ -7477,7 +7556,7 @@ function OhhwellsBridge() {
7477
7556
  }
7478
7557
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7479
7558
  const textEl = Array.from(
7480
- document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
7559
+ document.querySelectorAll(NON_MEDIA_SELECTOR)
7481
7560
  ).find((el) => {
7482
7561
  const er = el.getBoundingClientRect();
7483
7562
  return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
@@ -7602,7 +7681,9 @@ function OhhwellsBridge() {
7602
7681
  if (!el) return;
7603
7682
  const file = e.dataTransfer?.files?.[0];
7604
7683
  if (file) {
7605
- if (file.type.startsWith("image/")) {
7684
+ const wantsVideo = el.dataset.ohwEditable === "video";
7685
+ const accepted = wantsVideo ? file.type.startsWith("video/") : file.type.startsWith("image/");
7686
+ if (accepted) {
7606
7687
  const key = el.dataset.ohwKey ?? "";
7607
7688
  const { name, type: mimeType } = file;
7608
7689
  const r2 = el.getBoundingClientRect();
@@ -7618,6 +7699,30 @@ function OhhwellsBridge() {
7618
7699
  resumeAnimTracks();
7619
7700
  postToParentRef.current({ type: "ow:image-unhover" });
7620
7701
  };
7702
+ const handleVideoSettings = (e) => {
7703
+ if (e.data?.type !== "ow:video-settings") return;
7704
+ const { key, autoplay, muted } = e.data;
7705
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7706
+ const video = getVideoEl(el);
7707
+ if (!video) return;
7708
+ if (typeof autoplay === "boolean") video.autoplay = autoplay;
7709
+ if (typeof muted === "boolean") video.muted = muted;
7710
+ syncVideoPlayback(video);
7711
+ postToParentRef.current({
7712
+ type: "ow:change",
7713
+ nodes: [
7714
+ { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
7715
+ { key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
7716
+ ]
7717
+ });
7718
+ postToParentRef.current({
7719
+ type: "ow:video-settings-applied",
7720
+ key,
7721
+ autoplay: video.autoplay,
7722
+ muted: video.muted
7723
+ });
7724
+ });
7725
+ };
7621
7726
  const handleImageUrl = (e) => {
7622
7727
  if (e.data?.type !== "ow:image-url") return;
7623
7728
  const { key, url } = e.data;
@@ -7669,6 +7774,19 @@ function OhhwellsBridge() {
7669
7774
  requestAnimationFrame(() => onReady());
7670
7775
  }
7671
7776
  }
7777
+ } else if (el.dataset.ohwEditable === "video") {
7778
+ const video = getVideoEl(el);
7779
+ if (video) {
7780
+ found = true;
7781
+ const onReady = () => {
7782
+ video.onloadeddata = null;
7783
+ video.onerror = null;
7784
+ notify();
7785
+ };
7786
+ video.onloadeddata = onReady;
7787
+ video.onerror = onReady;
7788
+ applyVideoSrc(video, url);
7789
+ }
7672
7790
  }
7673
7791
  });
7674
7792
  if (!found) notify();
@@ -7735,12 +7853,16 @@ function OhhwellsBridge() {
7735
7853
  sectionsJson = val;
7736
7854
  continue;
7737
7855
  }
7856
+ if (applyVideoSettingNode(key, val)) continue;
7738
7857
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7739
7858
  if (el.dataset.ohwEditable === "image") {
7740
7859
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
7741
7860
  if (img) img.src = val;
7742
7861
  } else if (el.dataset.ohwEditable === "bg-image") {
7743
7862
  el.style.backgroundImage = `url('${val}')`;
7863
+ } else if (el.dataset.ohwEditable === "video") {
7864
+ const video = getVideoEl(el);
7865
+ if (video && video.src !== val) applyVideoSrc(video, val);
7744
7866
  } else if (el.dataset.ohwEditable === "link") {
7745
7867
  applyLinkHref(el, val);
7746
7868
  } else {
@@ -8001,7 +8123,7 @@ function OhhwellsBridge() {
8001
8123
  if (e.data?.type !== "ow:click-at") return;
8002
8124
  const { clientX, clientY } = e.data;
8003
8125
  const allImages = Array.from(
8004
- document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]')
8126
+ document.querySelectorAll(MEDIA_SELECTOR)
8005
8127
  ).filter((el) => !!el.closest("[data-ohw-editable-state]"));
8006
8128
  const stateCardImage = allImages.find((el) => {
8007
8129
  const ownerCard = el.closest("[data-ohw-editable-state]");
@@ -8020,9 +8142,7 @@ function OhhwellsBridge() {
8020
8142
  return;
8021
8143
  }
8022
8144
  const textEditable = Array.from(
8023
- document.querySelectorAll(
8024
- '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])'
8025
- )
8145
+ document.querySelectorAll(NON_MEDIA_SELECTOR)
8026
8146
  ).find((el) => {
8027
8147
  const r2 = el.getBoundingClientRect();
8028
8148
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
@@ -8050,6 +8170,7 @@ function OhhwellsBridge() {
8050
8170
  window.addEventListener("message", handleClearSchedulingWidget);
8051
8171
  window.addEventListener("message", handleRemoveSchedulingSection);
8052
8172
  window.addEventListener("message", handleImageUrl);
8173
+ window.addEventListener("message", handleVideoSettings);
8053
8174
  window.addEventListener("message", handleAnimLock);
8054
8175
  window.addEventListener("message", handleCanvasHeight);
8055
8176
  window.addEventListener("message", handleParentScroll);
@@ -8097,6 +8218,7 @@ function OhhwellsBridge() {
8097
8218
  window.removeEventListener("message", handleClearSchedulingWidget);
8098
8219
  window.removeEventListener("message", handleRemoveSchedulingSection);
8099
8220
  window.removeEventListener("message", handleImageUrl);
8221
+ window.removeEventListener("message", handleVideoSettings);
8100
8222
  window.removeEventListener("message", handleAnimLock);
8101
8223
  window.removeEventListener("message", handleCanvasHeight);
8102
8224
  window.removeEventListener("message", handleParentScroll);