@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.cjs CHANGED
@@ -5925,6 +5925,9 @@ function collectEditableNodes() {
5925
5925
  const url = raw.replace(/^url\(['"]?/, "").replace(/['"]?\)$/, "");
5926
5926
  return { key: el.dataset.ohwKey ?? "", type: "bg-image", text: url };
5927
5927
  }
5928
+ if (el.dataset.ohwEditable === "video") {
5929
+ return { key: el.dataset.ohwKey ?? "", type: "video", text: getVideoEl(el)?.src ?? "" };
5930
+ }
5928
5931
  if (el.dataset.ohwEditable === "link") {
5929
5932
  return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref(el) };
5930
5933
  }
@@ -5939,8 +5942,62 @@ function collectEditableNodes() {
5939
5942
  if (!key) return;
5940
5943
  nodes.push({ key, type: "link", text: getLinkHref(el) });
5941
5944
  });
5945
+ document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
5946
+ const key = el.dataset.ohwKey ?? "";
5947
+ const video = getVideoEl(el);
5948
+ if (!key || !video) return;
5949
+ nodes.push({ key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, type: "video-setting", text: String(video.autoplay) });
5950
+ nodes.push({ key: `${key}${VIDEO_MUTED_SUFFIX}`, type: "video-setting", text: String(video.muted) });
5951
+ });
5942
5952
  return nodes;
5943
5953
  }
5954
+ function isMediaEditable(el) {
5955
+ const t = el.dataset.ohwEditable;
5956
+ return t === "image" || t === "bg-image" || t === "video";
5957
+ }
5958
+ var MEDIA_SELECTOR = '[data-ohw-editable="image"], [data-ohw-editable="bg-image"], [data-ohw-editable="video"]';
5959
+ var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"])';
5960
+ function getVideoEl(el) {
5961
+ return el instanceof HTMLVideoElement ? el : el.querySelector("video");
5962
+ }
5963
+ var VIDEO_AUTOPLAY_SUFFIX = "__ohw_autoplay";
5964
+ var VIDEO_MUTED_SUFFIX = "__ohw_muted";
5965
+ function isBackgroundVideo(video) {
5966
+ return video.autoplay && video.muted;
5967
+ }
5968
+ function syncVideoPlayback(video) {
5969
+ const background = isBackgroundVideo(video);
5970
+ video.controls = !background;
5971
+ if (video.autoplay) void video.play().catch(() => {
5972
+ });
5973
+ else video.pause();
5974
+ }
5975
+ function applyVideoSrc(video, url) {
5976
+ const { autoplay, muted } = video;
5977
+ video.src = url;
5978
+ video.loop = true;
5979
+ video.playsInline = true;
5980
+ video.autoplay = autoplay;
5981
+ video.muted = muted;
5982
+ video.load();
5983
+ syncVideoPlayback(video);
5984
+ }
5985
+ function applyVideoSettingNode(key, val) {
5986
+ const isAutoplay = key.endsWith(VIDEO_AUTOPLAY_SUFFIX);
5987
+ const isMuted = key.endsWith(VIDEO_MUTED_SUFFIX);
5988
+ if (!isAutoplay && !isMuted) return false;
5989
+ const suffixLen = (isAutoplay ? VIDEO_AUTOPLAY_SUFFIX : VIDEO_MUTED_SUFFIX).length;
5990
+ const baseKey = key.slice(0, key.length - suffixLen);
5991
+ const on = val === "true";
5992
+ document.querySelectorAll(`[data-ohw-key="${baseKey}"]`).forEach((el) => {
5993
+ const video = getVideoEl(el);
5994
+ if (!video) return;
5995
+ if (isAutoplay) video.autoplay = on;
5996
+ else video.muted = on;
5997
+ syncVideoPlayback(video);
5998
+ });
5999
+ return true;
6000
+ }
5944
6001
  function applyLinkByKey(key, val) {
5945
6002
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
5946
6003
  if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
@@ -6929,6 +6986,7 @@ function OhhwellsBridge() {
6929
6986
  const imageLoads = [];
6930
6987
  for (const [key, val] of Object.entries(content)) {
6931
6988
  if (key === "__ohw_sections") continue;
6989
+ if (applyVideoSettingNode(key, val)) continue;
6932
6990
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6933
6991
  if (el.dataset.ohwEditable === "image") {
6934
6992
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6942,6 +7000,15 @@ function OhhwellsBridge() {
6942
7000
  } else if (el.dataset.ohwEditable === "bg-image") {
6943
7001
  const next = `url('${val}')`;
6944
7002
  if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
7003
+ } else if (el.dataset.ohwEditable === "video") {
7004
+ const video = getVideoEl(el);
7005
+ if (video && video.src !== val) {
7006
+ applyVideoSrc(video, val);
7007
+ imageLoads.push(new Promise((resolve) => {
7008
+ video.onloadeddata = () => resolve();
7009
+ video.onerror = () => resolve();
7010
+ }));
7011
+ }
6945
7012
  } else if (el.dataset.ohwEditable === "link") {
6946
7013
  applyLinkHref(el, val);
6947
7014
  } else if (el.innerHTML !== val) {
@@ -6990,6 +7057,7 @@ function OhhwellsBridge() {
6990
7057
  try {
6991
7058
  for (const [key, val] of Object.entries(content)) {
6992
7059
  if (key === "__ohw_sections") continue;
7060
+ if (applyVideoSettingNode(key, val)) continue;
6993
7061
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
6994
7062
  if (el.dataset.ohwEditable === "image") {
6995
7063
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
@@ -6997,6 +7065,9 @@ function OhhwellsBridge() {
6997
7065
  } else if (el.dataset.ohwEditable === "bg-image") {
6998
7066
  const next = `url('${val}')`;
6999
7067
  if (el.style.backgroundImage !== next) el.style.backgroundImage = next;
7068
+ } else if (el.dataset.ohwEditable === "video") {
7069
+ const video = getVideoEl(el);
7070
+ if (video && video.src !== val) applyVideoSrc(video, val);
7000
7071
  } else if (el.dataset.ohwEditable === "link") {
7001
7072
  applyLinkHref(el, val);
7002
7073
  } else if (el.innerHTML !== val) {
@@ -7100,8 +7171,9 @@ function OhhwellsBridge() {
7100
7171
  [data-ohw-editable] {
7101
7172
  display: block;
7102
7173
  }
7103
- [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]) { cursor: text !important; }
7174
+ [data-ohw-editable]:not([contenteditable]):not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]) { cursor: text !important; }
7104
7175
  [data-ohw-editable="image"], [data-ohw-editable="image"] *,
7176
+ [data-ohw-editable="video"], [data-ohw-editable="video"] *,
7105
7177
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
7106
7178
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
7107
7179
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
@@ -7168,7 +7240,7 @@ function OhhwellsBridge() {
7168
7240
  });
7169
7241
  return;
7170
7242
  }
7171
- if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
7243
+ if (isMediaEditable(editable)) {
7172
7244
  e.preventDefault();
7173
7245
  e.stopPropagation();
7174
7246
  postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
@@ -7271,7 +7343,7 @@ function OhhwellsBridge() {
7271
7343
  if (!editable) return;
7272
7344
  const selected = selectedElRef.current;
7273
7345
  if (selected && (selected === editable || selected.contains(editable))) return;
7274
- if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image" && !editable.hasAttribute("contenteditable")) {
7346
+ if (!isMediaEditable(editable) && !editable.hasAttribute("contenteditable")) {
7275
7347
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
7276
7348
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
7277
7349
  clearHrefKeyHover(hoverTarget);
@@ -7299,7 +7371,7 @@ function OhhwellsBridge() {
7299
7371
  if (!editable) return;
7300
7372
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
7301
7373
  if (related?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
7302
- if (editable.dataset.ohwEditable !== "image" && editable.dataset.ohwEditable !== "bg-image") {
7374
+ if (!isMediaEditable(editable)) {
7303
7375
  const hoverTarget = editable.closest("[data-ohw-href-key]") ?? editable;
7304
7376
  if (hoverTarget.hasAttribute("data-ohw-href-key")) {
7305
7377
  if (!related?.closest("[data-ohw-href-key]")) {
@@ -7349,11 +7421,16 @@ function OhhwellsBridge() {
7349
7421
  const r2 = getVisibleRect(imgEl);
7350
7422
  const hasTextOverlap = forceTextOverlap ?? false;
7351
7423
  hoveredImageHasTextOverlapRef.current = hasTextOverlap;
7424
+ const video = imgEl.dataset.ohwEditable === "video" ? getVideoEl(imgEl) : null;
7352
7425
  postToParentRef.current({
7353
7426
  type: "ow:image-hover",
7354
7427
  key: imgEl.dataset.ohwKey ?? "",
7428
+ // Lets the parent pick the file-picker `accept`, overlay label/icon and drop
7429
+ // mime-check without a parallel ow:video-* message channel.
7430
+ elementType: imgEl.dataset.ohwEditable ?? "image",
7355
7431
  rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
7356
7432
  hasTextOverlap,
7433
+ ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {},
7357
7434
  ...isDragOver ? { isDragOver: true } : {}
7358
7435
  });
7359
7436
  const track = imgEl.closest("[data-ohw-hover-pause]");
@@ -7361,7 +7438,7 @@ function OhhwellsBridge() {
7361
7438
  };
7362
7439
  const findImageAtPoint = (clientX, clientY, fromParentViewport) => {
7363
7440
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7364
- const images = Array.from(document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]'));
7441
+ const images = Array.from(document.querySelectorAll(MEDIA_SELECTOR));
7365
7442
  const matches = [];
7366
7443
  for (let i = images.length - 1; i >= 0; i--) {
7367
7444
  const el = images[i];
@@ -7377,7 +7454,9 @@ function OhhwellsBridge() {
7377
7454
  if (x >= r2.left && x <= r2.left + r2.width && y >= r2.top && y <= r2.top + r2.height) matches.push(el);
7378
7455
  }
7379
7456
  if (matches.length === 0) return null;
7380
- const imageTypeMatches = matches.filter((el) => el.dataset.ohwEditable === "image");
7457
+ const imageTypeMatches = matches.filter(
7458
+ (el) => el.dataset.ohwEditable === "image" || el.dataset.ohwEditable === "video"
7459
+ );
7381
7460
  const candidatePool = imageTypeMatches.length > 0 ? imageTypeMatches : matches;
7382
7461
  const smallest = candidatePool.reduce((best, el) => {
7383
7462
  const br = getVisibleRect(best);
@@ -7455,7 +7534,7 @@ function OhhwellsBridge() {
7455
7534
  }
7456
7535
  const isStateCardImage = !!imgEl.closest("[data-ohw-editable-state]");
7457
7536
  const textEditable = Array.from(
7458
- document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
7537
+ document.querySelectorAll(NON_MEDIA_SELECTOR)
7459
7538
  ).find((el) => {
7460
7539
  const er = el.getBoundingClientRect();
7461
7540
  return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
@@ -7536,7 +7615,7 @@ function OhhwellsBridge() {
7536
7615
  }
7537
7616
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
7538
7617
  const textEl = Array.from(
7539
- document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
7618
+ document.querySelectorAll(NON_MEDIA_SELECTOR)
7540
7619
  ).find((el) => {
7541
7620
  const er = el.getBoundingClientRect();
7542
7621
  return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
@@ -7661,7 +7740,9 @@ function OhhwellsBridge() {
7661
7740
  if (!el) return;
7662
7741
  const file = e.dataTransfer?.files?.[0];
7663
7742
  if (file) {
7664
- if (file.type.startsWith("image/")) {
7743
+ const wantsVideo = el.dataset.ohwEditable === "video";
7744
+ const accepted = wantsVideo ? file.type.startsWith("video/") : file.type.startsWith("image/");
7745
+ if (accepted) {
7665
7746
  const key = el.dataset.ohwKey ?? "";
7666
7747
  const { name, type: mimeType } = file;
7667
7748
  const r2 = el.getBoundingClientRect();
@@ -7677,6 +7758,30 @@ function OhhwellsBridge() {
7677
7758
  resumeAnimTracks();
7678
7759
  postToParentRef.current({ type: "ow:image-unhover" });
7679
7760
  };
7761
+ const handleVideoSettings = (e) => {
7762
+ if (e.data?.type !== "ow:video-settings") return;
7763
+ const { key, autoplay, muted } = e.data;
7764
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7765
+ const video = getVideoEl(el);
7766
+ if (!video) return;
7767
+ if (typeof autoplay === "boolean") video.autoplay = autoplay;
7768
+ if (typeof muted === "boolean") video.muted = muted;
7769
+ syncVideoPlayback(video);
7770
+ postToParentRef.current({
7771
+ type: "ow:change",
7772
+ nodes: [
7773
+ { key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`, text: String(video.autoplay) },
7774
+ { key: `${key}${VIDEO_MUTED_SUFFIX}`, text: String(video.muted) }
7775
+ ]
7776
+ });
7777
+ postToParentRef.current({
7778
+ type: "ow:video-settings-applied",
7779
+ key,
7780
+ autoplay: video.autoplay,
7781
+ muted: video.muted
7782
+ });
7783
+ });
7784
+ };
7680
7785
  const handleImageUrl = (e) => {
7681
7786
  if (e.data?.type !== "ow:image-url") return;
7682
7787
  const { key, url } = e.data;
@@ -7728,6 +7833,19 @@ function OhhwellsBridge() {
7728
7833
  requestAnimationFrame(() => onReady());
7729
7834
  }
7730
7835
  }
7836
+ } else if (el.dataset.ohwEditable === "video") {
7837
+ const video = getVideoEl(el);
7838
+ if (video) {
7839
+ found = true;
7840
+ const onReady = () => {
7841
+ video.onloadeddata = null;
7842
+ video.onerror = null;
7843
+ notify();
7844
+ };
7845
+ video.onloadeddata = onReady;
7846
+ video.onerror = onReady;
7847
+ applyVideoSrc(video, url);
7848
+ }
7731
7849
  }
7732
7850
  });
7733
7851
  if (!found) notify();
@@ -7794,12 +7912,16 @@ function OhhwellsBridge() {
7794
7912
  sectionsJson = val;
7795
7913
  continue;
7796
7914
  }
7915
+ if (applyVideoSettingNode(key, val)) continue;
7797
7916
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
7798
7917
  if (el.dataset.ohwEditable === "image") {
7799
7918
  const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
7800
7919
  if (img) img.src = val;
7801
7920
  } else if (el.dataset.ohwEditable === "bg-image") {
7802
7921
  el.style.backgroundImage = `url('${val}')`;
7922
+ } else if (el.dataset.ohwEditable === "video") {
7923
+ const video = getVideoEl(el);
7924
+ if (video && video.src !== val) applyVideoSrc(video, val);
7803
7925
  } else if (el.dataset.ohwEditable === "link") {
7804
7926
  applyLinkHref(el, val);
7805
7927
  } else {
@@ -8060,7 +8182,7 @@ function OhhwellsBridge() {
8060
8182
  if (e.data?.type !== "ow:click-at") return;
8061
8183
  const { clientX, clientY } = e.data;
8062
8184
  const allImages = Array.from(
8063
- document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]')
8185
+ document.querySelectorAll(MEDIA_SELECTOR)
8064
8186
  ).filter((el) => !!el.closest("[data-ohw-editable-state]"));
8065
8187
  const stateCardImage = allImages.find((el) => {
8066
8188
  const ownerCard = el.closest("[data-ohw-editable-state]");
@@ -8079,9 +8201,7 @@ function OhhwellsBridge() {
8079
8201
  return;
8080
8202
  }
8081
8203
  const textEditable = Array.from(
8082
- document.querySelectorAll(
8083
- '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])'
8084
- )
8204
+ document.querySelectorAll(NON_MEDIA_SELECTOR)
8085
8205
  ).find((el) => {
8086
8206
  const r2 = el.getBoundingClientRect();
8087
8207
  return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
@@ -8109,6 +8229,7 @@ function OhhwellsBridge() {
8109
8229
  window.addEventListener("message", handleClearSchedulingWidget);
8110
8230
  window.addEventListener("message", handleRemoveSchedulingSection);
8111
8231
  window.addEventListener("message", handleImageUrl);
8232
+ window.addEventListener("message", handleVideoSettings);
8112
8233
  window.addEventListener("message", handleAnimLock);
8113
8234
  window.addEventListener("message", handleCanvasHeight);
8114
8235
  window.addEventListener("message", handleParentScroll);
@@ -8156,6 +8277,7 @@ function OhhwellsBridge() {
8156
8277
  window.removeEventListener("message", handleClearSchedulingWidget);
8157
8278
  window.removeEventListener("message", handleRemoveSchedulingSection);
8158
8279
  window.removeEventListener("message", handleImageUrl);
8280
+ window.removeEventListener("message", handleVideoSettings);
8159
8281
  window.removeEventListener("message", handleAnimLock);
8160
8282
  window.removeEventListener("message", handleCanvasHeight);
8161
8283
  window.removeEventListener("message", handleParentScroll);