@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.cjs CHANGED
@@ -3435,7 +3435,9 @@ function ToggleGroupItem({
3435
3435
  Toggle,
3436
3436
  {
3437
3437
  pressed: groupValue === value,
3438
- onPressedChange: () => onValueChange(value),
3438
+ onPressedChange: (pressed) => {
3439
+ if (pressed) onValueChange(value);
3440
+ },
3439
3441
  className: cn(
3440
3442
  "text-foreground cursor-pointer hover:bg-transparent hover:text-foreground",
3441
3443
  "data-[state=on]:bg-background data-[state=on]:shadow-sm",
@@ -3830,7 +3832,7 @@ function StateToggle({
3830
3832
  left: rect.right - 8,
3831
3833
  transform: "translateX(-100%)"
3832
3834
  },
3833
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToggleGroupItem, { value: state, children: state }, state))
3835
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
3834
3836
  }
3835
3837
  );
3836
3838
  }
@@ -3840,20 +3842,19 @@ function OhhwellsBridge() {
3840
3842
  const router = (0, import_navigation.useRouter)();
3841
3843
  const searchParams = (0, import_navigation.useSearchParams)();
3842
3844
  const isEditMode = isEditSessionActive();
3843
- (0, import_react.useEffect)(() => {
3844
- if (!isEditMode) return;
3845
- const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
3846
- const preconnect2 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" });
3847
- 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" });
3848
- document.head.append(preconnect1, preconnect2, stylesheet);
3849
- return () => {
3850
- preconnect1.remove();
3851
- preconnect2.remove();
3852
- stylesheet.remove();
3853
- };
3854
- }, [isEditMode]);
3855
3845
  const [bridgeRoot, setBridgeRoot] = (0, import_react.useState)(null);
3856
3846
  (0, import_react.useEffect)(() => {
3847
+ const figtreeFontId = "ohw-figtree-font";
3848
+ if (!document.getElementById(figtreeFontId)) {
3849
+ const preconnect1 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.googleapis.com" });
3850
+ const preconnect2 = Object.assign(document.createElement("link"), { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" });
3851
+ const stylesheet = Object.assign(document.createElement("link"), {
3852
+ id: figtreeFontId,
3853
+ rel: "stylesheet",
3854
+ href: "https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap"
3855
+ });
3856
+ document.head.append(preconnect1, preconnect2, stylesheet);
3857
+ }
3857
3858
  const el = document.createElement("div");
3858
3859
  el.setAttribute("data-ohw-bridge-root", "");
3859
3860
  el.setAttribute("data-ohw-bridge", "");
@@ -3884,6 +3885,9 @@ function OhhwellsBridge() {
3884
3885
  const toolbarElRef = (0, import_react.useRef)(null);
3885
3886
  const glowElRef = (0, import_react.useRef)(null);
3886
3887
  const hoveredImageRef = (0, import_react.useRef)(null);
3888
+ const hoveredImageHasTextOverlapRef = (0, import_react.useRef)(false);
3889
+ const imageUnhoverTimerRef = (0, import_react.useRef)(null);
3890
+ const imageShowTimerRef = (0, import_react.useRef)(null);
3887
3891
  const editStylesRef = (0, import_react.useRef)(null);
3888
3892
  const activateRef = (0, import_react.useRef)(() => {
3889
3893
  });
@@ -3946,6 +3950,10 @@ function OhhwellsBridge() {
3946
3950
  const activate = (0, import_react.useCallback)((el) => {
3947
3951
  if (activeElRef.current === el) return;
3948
3952
  deactivate();
3953
+ if (hoveredImageRef.current) {
3954
+ hoveredImageRef.current = null;
3955
+ postToParentRef.current({ type: "ow:image-unhover" });
3956
+ }
3949
3957
  el.setAttribute("contenteditable", "true");
3950
3958
  el.removeAttribute("data-ohw-hovered");
3951
3959
  activeElRef.current = el;
@@ -3993,7 +4001,7 @@ function OhhwellsBridge() {
3993
4001
  }
3994
4002
  let cancelled = false;
3995
4003
  setFetchState("loading");
3996
- const apiUrl = globalThis.process?.env?.NEXT_PUBLIC_FLOWOPS_API_URL ?? "http://localhost:4005";
4004
+ const apiUrl = globalThis.process?.env?.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
3997
4005
  fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
3998
4006
  if (cancelled) return;
3999
4007
  const content = data?.content ?? {};
@@ -4120,6 +4128,7 @@ function OhhwellsBridge() {
4120
4128
  [data-ohw-editable][contenteditable] *::selection { background: ${PRIMARY}59 !important; }
4121
4129
  [data-ohw-editable-state], [data-ohw-editable-state] * { pointer-events: none !important; }
4122
4130
  [data-ohw-editable-state][data-ohw-active-state] [data-ohw-editable] { pointer-events: auto !important; }
4131
+ [data-ohw-editable-state][data-ohw-active-state][data-ohw-editable] { pointer-events: auto !important; }
4123
4132
  `;
4124
4133
  const forceHover = document.createElement("style");
4125
4134
  forceHover.setAttribute("data-ohw-active-state-style", "");
@@ -4146,10 +4155,12 @@ function OhhwellsBridge() {
4146
4155
  if (editable) {
4147
4156
  if (editable.dataset.ohwEditable === "image" || editable.dataset.ohwEditable === "bg-image") {
4148
4157
  e.preventDefault();
4158
+ e.stopPropagation();
4149
4159
  postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "" });
4150
4160
  return;
4151
4161
  }
4152
4162
  e.preventDefault();
4163
+ e.stopPropagation();
4153
4164
  activateRef.current(editable);
4154
4165
  return;
4155
4166
  }
@@ -4212,12 +4223,15 @@ function OhhwellsBridge() {
4212
4223
  }
4213
4224
  return { top, left, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };
4214
4225
  };
4215
- const postImageHover = (imgEl, isDragOver = false) => {
4226
+ const postImageHover = (imgEl, isDragOver = false, forceTextOverlap) => {
4216
4227
  const r2 = getVisibleRect(imgEl);
4228
+ const hasTextOverlap = forceTextOverlap ?? false;
4229
+ hoveredImageHasTextOverlapRef.current = hasTextOverlap;
4217
4230
  postToParentRef.current({
4218
4231
  type: "ow:image-hover",
4219
4232
  key: imgEl.dataset.ohwKey ?? "",
4220
4233
  rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
4234
+ hasTextOverlap,
4221
4235
  ...isDragOver ? { isDragOver: true } : {}
4222
4236
  });
4223
4237
  const track = imgEl.closest("[data-ohw-hover-pause]");
@@ -4264,7 +4278,7 @@ function OhhwellsBridge() {
4264
4278
  }
4265
4279
  return smallest;
4266
4280
  };
4267
- const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false, fromOverlay = false) => {
4281
+ const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
4268
4282
  const toggleEl = document.querySelector("[data-ohw-state-toggle]");
4269
4283
  if (toggleEl) {
4270
4284
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
@@ -4290,33 +4304,52 @@ function OhhwellsBridge() {
4290
4304
  }
4291
4305
  return;
4292
4306
  }
4293
- const topEditable = topEl?.closest("[data-ohw-editable]");
4294
- if (topEditable && topEditable.dataset.ohwEditable !== "image" && topEditable.dataset.ohwEditable !== "bg-image") {
4295
- if (topEditable.hasAttribute("contenteditable") && fromOverlay) return;
4296
- if (hoveredImageRef.current) {
4297
- hoveredImageRef.current = null;
4298
- resumeAnimTracks();
4299
- postToParentRef.current({ type: "ow:image-unhover" });
4300
- }
4301
- document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4302
- topEditable.setAttribute("data-ohw-hovered", "");
4303
- return;
4307
+ const isStateCardImage = !!imgEl.closest("[data-ohw-editable-state]");
4308
+ const textEditable = Array.from(
4309
+ document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
4310
+ ).find((el) => {
4311
+ const er = el.getBoundingClientRect();
4312
+ return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
4313
+ });
4314
+ if (imageUnhoverTimerRef.current) {
4315
+ clearTimeout(imageUnhoverTimerRef.current);
4316
+ imageUnhoverTimerRef.current = null;
4304
4317
  }
4305
- const activeHovered = document.querySelector("[data-ohw-hovered]");
4306
- if (activeHovered) {
4307
- const outlinePad = 8;
4308
- const hr = activeHovered.getBoundingClientRect();
4309
- if (x >= hr.left - outlinePad && x <= hr.right + outlinePad && y >= hr.top - outlinePad && y <= hr.bottom + outlinePad) {
4318
+ if (imageShowTimerRef.current) {
4319
+ clearTimeout(imageShowTimerRef.current);
4320
+ imageShowTimerRef.current = null;
4321
+ }
4322
+ if (textEditable) {
4323
+ if (textEditable.hasAttribute("contenteditable")) {
4310
4324
  if (hoveredImageRef.current) {
4311
4325
  hoveredImageRef.current = null;
4326
+ hoveredImageHasTextOverlapRef.current = false;
4312
4327
  resumeAnimTracks();
4313
4328
  postToParentRef.current({ type: "ow:image-unhover" });
4314
4329
  }
4315
4330
  return;
4316
4331
  }
4332
+ if (isStateCardImage) {
4333
+ if (imgEl !== hoveredImageRef.current || !hoveredImageHasTextOverlapRef.current) {
4334
+ hoveredImageRef.current = imgEl;
4335
+ postImageHover(imgEl, isDragOver, true);
4336
+ }
4337
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4338
+ textEditable.setAttribute("data-ohw-hovered", "");
4339
+ return;
4340
+ }
4341
+ if (hoveredImageRef.current) {
4342
+ hoveredImageRef.current = null;
4343
+ hoveredImageHasTextOverlapRef.current = false;
4344
+ resumeAnimTracks();
4345
+ postToParentRef.current({ type: "ow:image-unhover" });
4346
+ }
4347
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4348
+ textEditable.setAttribute("data-ohw-hovered", "");
4349
+ return;
4317
4350
  }
4318
4351
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4319
- if (imgEl !== hoveredImageRef.current) {
4352
+ if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
4320
4353
  hoveredImageRef.current = imgEl;
4321
4354
  postImageHover(imgEl, isDragOver);
4322
4355
  } else if (isDragOver) {
@@ -4325,9 +4358,29 @@ function OhhwellsBridge() {
4325
4358
  } else {
4326
4359
  const inIframeView = clientX >= 0 && clientY >= 0 && clientX <= window.innerWidth && clientY <= window.innerHeight;
4327
4360
  if (!inIframeView) return;
4328
- hoveredImageRef.current = null;
4329
- resumeAnimTracks();
4330
- postToParentRef.current({ type: "ow:image-unhover" });
4361
+ if (imageUnhoverTimerRef.current) {
4362
+ clearTimeout(imageUnhoverTimerRef.current);
4363
+ imageUnhoverTimerRef.current = null;
4364
+ }
4365
+ if (imageShowTimerRef.current) {
4366
+ clearTimeout(imageShowTimerRef.current);
4367
+ imageShowTimerRef.current = null;
4368
+ }
4369
+ if (hoveredImageRef.current) {
4370
+ hoveredImageRef.current = null;
4371
+ hoveredImageHasTextOverlapRef.current = false;
4372
+ resumeAnimTracks();
4373
+ postToParentRef.current({ type: "ow:image-unhover" });
4374
+ }
4375
+ const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
4376
+ const textEl = Array.from(
4377
+ document.querySelectorAll('[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])')
4378
+ ).find((el) => {
4379
+ const er = el.getBoundingClientRect();
4380
+ return x >= er.left && x <= er.right && y >= er.top && y <= er.bottom;
4381
+ });
4382
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
4383
+ if (textEl && !textEl.hasAttribute("contenteditable")) textEl.setAttribute("data-ohw-hovered", "");
4331
4384
  }
4332
4385
  };
4333
4386
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
@@ -4346,15 +4399,18 @@ function OhhwellsBridge() {
4346
4399
  }) ?? null;
4347
4400
  const currentLocked = activeStateElRef.current?.hasAttribute("data-ohw-active-state") ? activeStateElRef.current : null;
4348
4401
  const active = found ?? currentLocked;
4402
+ const prev = activeStateElRef.current;
4349
4403
  activeStateElRef.current = active;
4350
- document.querySelectorAll("[data-ohw-state-hovered]").forEach((el) => el.removeAttribute("data-ohw-state-hovered"));
4351
- if (active) {
4352
- if (active.querySelector("[data-ohw-state-view]")) active.setAttribute("data-ohw-state-hovered", "");
4353
- const states = deriveStates(active.dataset.ohwEditableState ?? "");
4354
- 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";
4355
- setToggleState({ rect: active.getBoundingClientRect(), activeState, states });
4356
- } else {
4357
- setToggleState(null);
4404
+ if (active !== prev) {
4405
+ if (prev) prev.removeAttribute("data-ohw-state-hovered");
4406
+ if (active) {
4407
+ if (active.querySelector("[data-ohw-state-view]")) active.setAttribute("data-ohw-state-hovered", "");
4408
+ const states = deriveStates(active.dataset.ohwEditableState ?? "");
4409
+ 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";
4410
+ setToggleState({ rect: active.getBoundingClientRect(), activeState, states });
4411
+ } else {
4412
+ setToggleState(null);
4413
+ }
4358
4414
  }
4359
4415
  };
4360
4416
  const handleMouseMove = (e) => {
@@ -4364,9 +4420,9 @@ function OhhwellsBridge() {
4364
4420
  };
4365
4421
  const handlePointerSync = (e) => {
4366
4422
  if (e.data?.type !== "ow:pointer-sync") return;
4367
- const { clientX, clientY, fromOverlay } = e.data;
4423
+ const { clientX, clientY } = e.data;
4368
4424
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
4369
- probeImageAt(clientX, clientY, false, false, fromOverlay ?? false);
4425
+ probeImageAt(clientX, clientY);
4370
4426
  probeHoverCardsAt(clientX, clientY);
4371
4427
  };
4372
4428
  const handleDragOver = (e) => {
@@ -4379,7 +4435,8 @@ function OhhwellsBridge() {
4379
4435
  e.dataTransfer.dropEffect = "copy";
4380
4436
  if (hoveredImageRef.current !== el) {
4381
4437
  hoveredImageRef.current = el;
4382
- postImageHover(el, true);
4438
+ const isStateCard = !!el.closest("[data-ohw-editable-state]");
4439
+ postImageHover(el, true, isStateCard ? true : void 0);
4383
4440
  }
4384
4441
  };
4385
4442
  const handleDragLeave = (e) => {
@@ -4565,11 +4622,13 @@ function OhhwellsBridge() {
4565
4622
  setToggleState((prev) => prev ? { ...prev, rect } : null);
4566
4623
  }
4567
4624
  if (hoveredImageRef.current) {
4568
- const r2 = hoveredImageRef.current.getBoundingClientRect();
4625
+ const el = hoveredImageRef.current;
4626
+ const r2 = el.getBoundingClientRect();
4569
4627
  postToParentRef.current({
4570
4628
  type: "ow:image-hover",
4571
- key: hoveredImageRef.current.dataset.ohwKey ?? "",
4572
- rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height }
4629
+ key: el.dataset.ohwKey ?? "",
4630
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
4631
+ hasTextOverlap: hoveredImageHasTextOverlapRef.current
4573
4632
  });
4574
4633
  }
4575
4634
  };
@@ -4643,12 +4702,49 @@ function OhhwellsBridge() {
4643
4702
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
4644
4703
  if (activeElRef.current) applyToolbarPos(activeElRef.current.getBoundingClientRect());
4645
4704
  };
4705
+ const handleClickAt = (e) => {
4706
+ if (e.data?.type !== "ow:click-at") return;
4707
+ const { clientX, clientY } = e.data;
4708
+ const allImages = Array.from(
4709
+ document.querySelectorAll('[data-ohw-editable="image"], [data-ohw-editable="bg-image"]')
4710
+ ).filter((el) => !!el.closest("[data-ohw-editable-state]"));
4711
+ const stateCardImage = allImages.find((el) => {
4712
+ const ownerCard = el.closest("[data-ohw-editable-state]");
4713
+ if (ownerCard) {
4714
+ const inHoverState = ownerCard.getAttribute("data-ohw-active-state") === "hover";
4715
+ const elState = el.dataset.ohwState;
4716
+ if (el === ownerCard && inHoverState) return false;
4717
+ if (elState === "default" && inHoverState) return false;
4718
+ if (elState === "hover" && !inHoverState) return false;
4719
+ }
4720
+ const r2 = el.getBoundingClientRect();
4721
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
4722
+ });
4723
+ if (stateCardImage) {
4724
+ postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "" });
4725
+ return;
4726
+ }
4727
+ const textEditable = Array.from(
4728
+ document.querySelectorAll(
4729
+ '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"])'
4730
+ )
4731
+ ).find((el) => {
4732
+ const r2 = el.getBoundingClientRect();
4733
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
4734
+ });
4735
+ if (textEditable) {
4736
+ activateRef.current(textEditable);
4737
+ return;
4738
+ }
4739
+ deactivateRef.current();
4740
+ };
4646
4741
  window.addEventListener("message", handleSave);
4647
4742
  window.addEventListener("message", handleImageUrl);
4648
4743
  window.addEventListener("message", handleAnimLock);
4649
4744
  window.addEventListener("message", handleCanvasHeight);
4650
4745
  window.addEventListener("message", handleParentScroll);
4651
4746
  window.addEventListener("message", handlePointerSync);
4747
+ window.addEventListener("message", handleClickAt);
4652
4748
  document.addEventListener("click", handleClick, true);
4653
4749
  document.addEventListener("paste", handlePaste, true);
4654
4750
  document.addEventListener("input", handleInput, true);
@@ -4682,10 +4778,13 @@ function OhhwellsBridge() {
4682
4778
  window.removeEventListener("message", handleCanvasHeight);
4683
4779
  window.removeEventListener("message", handleParentScroll);
4684
4780
  window.removeEventListener("message", handlePointerSync);
4781
+ window.removeEventListener("message", handleClickAt);
4685
4782
  window.removeEventListener("message", handleHydrate);
4686
4783
  window.removeEventListener("message", handleDeactivate);
4687
4784
  autoSaveTimers.current.forEach(clearTimeout);
4688
4785
  autoSaveTimers.current.clear();
4786
+ if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
4787
+ if (imageShowTimerRef.current) clearTimeout(imageShowTimerRef.current);
4689
4788
  };
4690
4789
  }, [isEditMode, refreshStateRules]);
4691
4790
  (0, import_react.useEffect)(() => {