@ohhwells/bridge 0.1.14 → 0.1.15

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
@@ -3396,7 +3396,9 @@ function ToggleGroupItem({
3396
3396
  Toggle,
3397
3397
  {
3398
3398
  pressed: groupValue === value,
3399
- onPressedChange: () => onValueChange(value),
3399
+ onPressedChange: (pressed) => {
3400
+ if (pressed) onValueChange(value);
3401
+ },
3400
3402
  className: cn(
3401
3403
  "text-foreground cursor-pointer hover:bg-transparent hover:text-foreground",
3402
3404
  "data-[state=on]:bg-background data-[state=on]:shadow-sm",
@@ -3791,7 +3793,7 @@ function StateToggle({
3791
3793
  left: rect.right - 8,
3792
3794
  transform: "translateX(-100%)"
3793
3795
  },
3794
- children: states.map((state) => /* @__PURE__ */ jsx3(ToggleGroupItem, { value: state, children: state }, state))
3796
+ children: states.map((state) => /* @__PURE__ */ jsx3(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
3795
3797
  }
3796
3798
  );
3797
3799
  }
@@ -3801,20 +3803,19 @@ function OhhwellsBridge() {
3801
3803
  const router = useRouter();
3802
3804
  const searchParams = useSearchParams();
3803
3805
  const isEditMode = isEditSessionActive();
3804
- useEffect(() => {
3805
- if (!isEditMode) return;
3806
- const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
3807
- const preconnect2 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" });
3808
- const stylesheet = Object.assign(document.createElement("link"), { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap" });
3809
- document.head.append(preconnect1, preconnect2, stylesheet);
3810
- return () => {
3811
- preconnect1.remove();
3812
- preconnect2.remove();
3813
- stylesheet.remove();
3814
- };
3815
- }, [isEditMode]);
3816
3806
  const [bridgeRoot, setBridgeRoot] = useState(null);
3817
3807
  useEffect(() => {
3808
+ const figtreeFontId = "ohw-figtree-font";
3809
+ if (!document.getElementById(figtreeFontId)) {
3810
+ const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
3811
+ const preconnect2 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" });
3812
+ const stylesheet = Object.assign(document.createElement("link"), {
3813
+ id: figtreeFontId,
3814
+ rel: "stylesheet",
3815
+ href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap"
3816
+ });
3817
+ document.head.append(preconnect1, preconnect2, stylesheet);
3818
+ }
3818
3819
  const el = document.createElement("div");
3819
3820
  el.setAttribute("data-ohw-bridge-root", "");
3820
3821
  el.setAttribute("data-ohw-bridge", "");
@@ -3845,6 +3846,9 @@ function OhhwellsBridge() {
3845
3846
  const toolbarElRef = useRef(null);
3846
3847
  const glowElRef = useRef(null);
3847
3848
  const hoveredImageRef = useRef(null);
3849
+ const hoveredImageHasTextOverlapRef = useRef(false);
3850
+ const imageUnhoverTimerRef = useRef(null);
3851
+ const imageShowTimerRef = useRef(null);
3848
3852
  const editStylesRef = useRef(null);
3849
3853
  const activateRef = useRef(() => {
3850
3854
  });
@@ -3907,6 +3911,10 @@ function OhhwellsBridge() {
3907
3911
  const activate = useCallback((el) => {
3908
3912
  if (activeElRef.current === el) return;
3909
3913
  deactivate();
3914
+ if (hoveredImageRef.current) {
3915
+ hoveredImageRef.current = null;
3916
+ postToParentRef.current({ type: "ow:image-unhover" });
3917
+ }
3910
3918
  el.setAttribute("contenteditable", "true");
3911
3919
  el.removeAttribute("data-ohw-hovered");
3912
3920
  activeElRef.current = el;
@@ -3954,7 +3962,7 @@ function OhhwellsBridge() {
3954
3962
  }
3955
3963
  let cancelled = false;
3956
3964
  setFetchState("loading");
3957
- const apiUrl = globalThis.process?.env?.NEXT_PUBLIC_FLOWOPS_API_URL ?? "http://localhost:4005";
3965
+ const apiUrl = globalThis.process?.env?.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
3958
3966
  fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
3959
3967
  if (cancelled) return;
3960
3968
  const content = data?.content ?? {};
@@ -4081,6 +4089,7 @@ function OhhwellsBridge() {
4081
4089
  [data-ohw-editable][contenteditable] *::selection { background: ${PRIMARY}59 !important; }
4082
4090
  [data-ohw-editable-state], [data-ohw-editable-state] * { pointer-events: none !important; }
4083
4091
  [data-ohw-editable-state][data-ohw-active-state] [data-ohw-editable] { pointer-events: auto !important; }
4092
+ [data-ohw-editable-state][data-ohw-active-state][data-ohw-editable] { pointer-events: auto !important; }
4084
4093
  `;
4085
4094
  const forceHover = document.createElement("style");
4086
4095
  forceHover.setAttribute("data-ohw-active-state-style", "");
@@ -4107,10 +4116,12 @@ function OhhwellsBridge() {
4107
4116
  if (editable) {
4108
4117
  if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
4109
4118
  e.preventDefault();
4119
+ e.stopPropagation();
4110
4120
  postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
4111
4121
  return;
4112
4122
  }
4113
4123
  e.preventDefault();
4124
+ e.stopPropagation();
4114
4125
  activateRef.current(editable);
4115
4126
  return;
4116
4127
  }
@@ -4173,12 +4184,15 @@ function OhhwellsBridge() {
4173
4184
  }
4174
4185
  return { top, left, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
4175
4186
  };
4176
- const postImageHover = (imgEl, isDragOver = false) => {
4187
+ const postImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
4177
4188
  const r2 = getVisibleRect(imgEl);
4189
+ const hasTextOverlap = forceTextOverlap ?? false;
4190
+ hoveredImageHasTextOverlapRef.current = hasTextOverlap;
4178
4191
  postToParentRef.current({
4179
4192
  type: "ow:image-hover",
4180
4193
  key: imgEl.dataset.ohwKey ?? "",
4181
4194
  rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
4195
+ hasTextOverlap,
4182
4196
  ...isDragOver ? { isDragOver: true } : {}
4183
4197
  });
4184
4198
  const track = imgEl.closest("[data-ohw-hover-pause]");
@@ -4225,7 +4239,7 @@ function OhhwellsBridge() {
4225
4239
  }
4226
4240
  return smallest;
4227
4241
  };
4228
- const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false, fromOverlay = false) => {
4242
+ const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
4229
4243
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
4230
4244
  if (toggleEl) {
4231
4245
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
@@ -4251,33 +4265,52 @@ function OhhwellsBridge() {
4251
4265
  }
4252
4266
  return;
4253
4267
  }
4254
- const topEditable = topEl?.closest("[data-ohw-editable]");
4255
- if (topEditable && topEditable.dataset.ohwEditable !== "image" && topEditable.dataset.ohwEditable !== "bg-image") {
4256
- if (topEditable.hasAttribute("contenteditable") && fromOverlay) return;
4257
- if (hoveredImageRef.current) {
4258
- hoveredImageRef.current = null;
4259
- resumeAnimTracks();
4260
- postToParentRef.current({ type: "ow:image-unhover" });
4261
- }
4262
- document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4263
- topEditable.setAttribute("data-ohw-hovered", "");
4264
- return;
4268
+ const isStateCardImage = !!imgEl.closest("[data-ohw-editable-state]");
4269
+ const textEditable = Array.from(
4270
+ document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
4271
+ ).find((el) => {
4272
+ const er = el.getBoundingClientRect();
4273
+ return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
4274
+ });
4275
+ if (imageUnhoverTimerRef.current) {
4276
+ clearTimeout(imageUnhoverTimerRef.current);
4277
+ imageUnhoverTimerRef.current = null;
4265
4278
  }
4266
- const activeHovered = document.querySelector("[data-ohw-hovered]");
4267
- if (activeHovered) {
4268
- const outlinePad = 8;
4269
- const hr = activeHovered.getBoundingClientRect();
4270
- if (x >= hr.left - outlinePad && x <= hr.right + outlinePad && y >= hr.top - outlinePad && y <= hr.bottom + outlinePad) {
4279
+ if (imageShowTimerRef.current) {
4280
+ clearTimeout(imageShowTimerRef.current);
4281
+ imageShowTimerRef.current = null;
4282
+ }
4283
+ if (textEditable) {
4284
+ if (textEditable.hasAttribute("contenteditable")) {
4271
4285
  if (hoveredImageRef.current) {
4272
4286
  hoveredImageRef.current = null;
4287
+ hoveredImageHasTextOverlapRef.current = false;
4273
4288
  resumeAnimTracks();
4274
4289
  postToParentRef.current({ type: "ow:image-unhover" });
4275
4290
  }
4276
4291
  return;
4277
4292
  }
4293
+ if (isStateCardImage) {
4294
+ if (imgEl !== hoveredImageRef.current || !hoveredImageHasTextOverlapRef.current) {
4295
+ hoveredImageRef.current = imgEl;
4296
+ postImageHover(imgEl, isDragOver, true);
4297
+ }
4298
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4299
+ textEditable.setAttribute("data-ohw-hovered", "");
4300
+ return;
4301
+ }
4302
+ if (hoveredImageRef.current) {
4303
+ hoveredImageRef.current = null;
4304
+ hoveredImageHasTextOverlapRef.current = false;
4305
+ resumeAnimTracks();
4306
+ postToParentRef.current({ type: "ow:image-unhover" });
4307
+ }
4308
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4309
+ textEditable.setAttribute("data-ohw-hovered", "");
4310
+ return;
4278
4311
  }
4279
4312
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4280
- if (imgEl !== hoveredImageRef.current) {
4313
+ if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
4281
4314
  hoveredImageRef.current = imgEl;
4282
4315
  postImageHover(imgEl, isDragOver);
4283
4316
  } else if (isDragOver) {
@@ -4286,9 +4319,29 @@ function OhhwellsBridge() {
4286
4319
  } else {
4287
4320
  const inIframeView = clientX >= 0 && clientY >= 0 && clientX <= window.innerWidth && clientY <= window.innerHeight;
4288
4321
  if (!inIframeView) return;
4289
- hoveredImageRef.current = null;
4290
- resumeAnimTracks();
4291
- postToParentRef.current({ type: "ow:image-unhover" });
4322
+ if (imageUnhoverTimerRef.current) {
4323
+ clearTimeout(imageUnhoverTimerRef.current);
4324
+ imageUnhoverTimerRef.current = null;
4325
+ }
4326
+ if (imageShowTimerRef.current) {
4327
+ clearTimeout(imageShowTimerRef.current);
4328
+ imageShowTimerRef.current = null;
4329
+ }
4330
+ if (hoveredImageRef.current) {
4331
+ hoveredImageRef.current = null;
4332
+ hoveredImageHasTextOverlapRef.current = false;
4333
+ resumeAnimTracks();
4334
+ postToParentRef.current({ type: "ow:image-unhover" });
4335
+ }
4336
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
4337
+ const textEl = Array.from(
4338
+ document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
4339
+ ).find((el) => {
4340
+ const er = el.getBoundingClientRect();
4341
+ return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
4342
+ });
4343
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4344
+ if (textEl && !textEl.hasAttribute("contenteditable")) textEl.setAttribute("data-ohw-hovered", "");
4292
4345
  }
4293
4346
  };
4294
4347
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
@@ -4307,15 +4360,18 @@ function OhhwellsBridge() {
4307
4360
  }) ?? null;
4308
4361
  const currentLocked = activeStateElRef.current?.hasAttribute("data-ohw-active-state") ? activeStateElRef.current : null;
4309
4362
  const active = found ?? currentLocked;
4363
+ const prev = activeStateElRef.current;
4310
4364
  activeStateElRef.current = active;
4311
- document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
4312
- if (active) {
4313
- if (active.querySelector("[data-ohw-state-view]")) active.setAttribute("data-ohw-state-hovered", "");
4314
- const states = deriveStates(active.dataset.ohwEditableState ?? "");
4315
- const activeState = active.hasAttribute("data-ohw-active-state") ? (active.getAttribute("data-ohw-active-state") ?? "Default").charAt(0).toUpperCase() + (active.getAttribute("data-ohw-active-state") ?? "").slice(1) : "Default";
4316
- setToggleState({ rect: active.getBoundingClientRect(), activeState, states });
4317
- } else {
4318
- setToggleState(null);
4365
+ if (active !== prev) {
4366
+ if (prev) prev.removeAttribute("data-ohw-state-hovered");
4367
+ if (active) {
4368
+ if (active.querySelector("[data-ohw-state-view]")) active.setAttribute("data-ohw-state-hovered", "");
4369
+ const states = deriveStates(active.dataset.ohwEditableState ?? "");
4370
+ const activeState = active.hasAttribute("data-ohw-active-state") ? (active.getAttribute("data-ohw-active-state") ?? "Default").charAt(0).toUpperCase() + (active.getAttribute("data-ohw-active-state") ?? "").slice(1) : "Default";
4371
+ setToggleState({ rect: active.getBoundingClientRect(), activeState, states });
4372
+ } else {
4373
+ setToggleState(null);
4374
+ }
4319
4375
  }
4320
4376
  };
4321
4377
  const handleMouseMove = (e) => {
@@ -4325,9 +4381,9 @@ function OhhwellsBridge() {
4325
4381
  };
4326
4382
  const handlePointerSync = (e) => {
4327
4383
  if (e.data?.type !== "ow:pointer-sync") return;
4328
- const { clientX, clientY, fromOverlay } = e.data;
4384
+ const { clientX, clientY } = e.data;
4329
4385
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
4330
- probeImageAt(clientX, clientY, false, false, fromOverlay ?? false);
4386
+ probeImageAt(clientX, clientY);
4331
4387
  probeHoverCardsAt(clientX, clientY);
4332
4388
  };
4333
4389
  const handleDragOver = (e) => {
@@ -4340,7 +4396,8 @@ function OhhwellsBridge() {
4340
4396
  e.dataTransfer.dropEffect = "copy";
4341
4397
  if (hoveredImageRef.current !== el) {
4342
4398
  hoveredImageRef.current = el;
4343
- postImageHover(el, true);
4399
+ const isStateCard = !!el.closest("[data-ohw-editable-state]");
4400
+ postImageHover(el, true, isStateCard ? true : void 0);
4344
4401
  }
4345
4402
  };
4346
4403
  const handleDragLeave = (e) => {
@@ -4526,11 +4583,13 @@ function OhhwellsBridge() {
4526
4583
  setToggleState((prev) => prev ? { ...prev, rect } : null);
4527
4584
  }
4528
4585
  if (hoveredImageRef.current) {
4529
- const r2 = hoveredImageRef.current.getBoundingClientRect();
4586
+ const el = hoveredImageRef.current;
4587
+ const r2 = el.getBoundingClientRect();
4530
4588
  postToParentRef.current({
4531
4589
  type: "ow:image-hover",
4532
- key: hoveredImageRef.current.dataset.ohwKey ?? "",
4533
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height }
4590
+ key: el.dataset.ohwKey ?? "",
4591
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
4592
+ hasTextOverlap: hoveredImageHasTextOverlapRef.current
4534
4593
  });
4535
4594
  }
4536
4595
  };
@@ -4604,12 +4663,49 @@ function OhhwellsBridge() {
4604
4663
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
4605
4664
  if (activeElRef.current) applyToolbarPos(activeElRef.current.getBoundingClientRect());
4606
4665
  };
4666
+ const handleClickAt = (e) => {
4667
+ if (e.data?.type !== "ow:click-at") return;
4668
+ const { clientX, clientY } = e.data;
4669
+ const allImages = Array.from(
4670
+ document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]')
4671
+ ).filter((el) => !!el.closest("[data-ohw-editable-state]"));
4672
+ const stateCardImage = allImages.find((el) => {
4673
+ const ownerCard = el.closest("[data-ohw-editable-state]");
4674
+ if (ownerCard) {
4675
+ const inHoverState = ownerCard.getAttribute("data-ohw-active-state") === "hover";
4676
+ const elState = el.dataset.ohwState;
4677
+ if (el === ownerCard && inHoverState) return false;
4678
+ if (elState === "default" && inHoverState) return false;
4679
+ if (elState === "hover" && !inHoverState) return false;
4680
+ }
4681
+ const r2 = el.getBoundingClientRect();
4682
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
4683
+ });
4684
+ if (stateCardImage) {
4685
+ postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "" });
4686
+ return;
4687
+ }
4688
+ const textEditable = Array.from(
4689
+ document.querySelectorAll(
4690
+ '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])'
4691
+ )
4692
+ ).find((el) => {
4693
+ const r2 = el.getBoundingClientRect();
4694
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
4695
+ });
4696
+ if (textEditable) {
4697
+ activateRef.current(textEditable);
4698
+ return;
4699
+ }
4700
+ deactivateRef.current();
4701
+ };
4607
4702
  window.addEventListener("message", handleSave);
4608
4703
  window.addEventListener("message", handleImageUrl);
4609
4704
  window.addEventListener("message", handleAnimLock);
4610
4705
  window.addEventListener("message", handleCanvasHeight);
4611
4706
  window.addEventListener("message", handleParentScroll);
4612
4707
  window.addEventListener("message", handlePointerSync);
4708
+ window.addEventListener("message", handleClickAt);
4613
4709
  document.addEventListener("click", handleClick, true);
4614
4710
  document.addEventListener("paste", handlePaste, true);
4615
4711
  document.addEventListener("input", handleInput, true);
@@ -4643,10 +4739,13 @@ function OhhwellsBridge() {
4643
4739
  window.removeEventListener("message", handleCanvasHeight);
4644
4740
  window.removeEventListener("message", handleParentScroll);
4645
4741
  window.removeEventListener("message", handlePointerSync);
4742
+ window.removeEventListener("message", handleClickAt);
4646
4743
  window.removeEventListener("message", handleHydrate);
4647
4744
  window.removeEventListener("message", handleDeactivate);
4648
4745
  autoSaveTimers.current.forEach(clearTimeout);
4649
4746
  autoSaveTimers.current.clear();
4747
+ if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
4748
+ if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
4650
4749
  };
4651
4750
  }, [isEditMode, refreshStateRules]);
4652
4751
  useEffect(() => {