@ohhwells/bridge 0.1.71-next.216 → 0.1.71-next.217

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
@@ -6791,6 +6791,7 @@ function ItemActionToolbar({
6791
6791
  var import_react7 = require("react");
6792
6792
  var import_jsx_runtime12 = require("react/jsx-runtime");
6793
6793
  var PRIMARY = "var(--ohw-primary, #0885FE)";
6794
+ var CHROME_RADIUS = 6;
6794
6795
  var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
6795
6796
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
6796
6797
  var TOOLBAR_EDGE_MARGIN = 4;
@@ -6985,15 +6986,16 @@ function ItemInteractionLayer({
6985
6986
  "div",
6986
6987
  {
6987
6988
  "aria-hidden": true,
6988
- className: "absolute inset-0 rounded-lg",
6989
- style: getChromeStyle(state)
6989
+ className: "absolute inset-0",
6990
+ style: { ...getChromeStyle(state), borderRadius: CHROME_RADIUS }
6990
6991
  }
6991
6992
  ),
6992
6993
  itemDragEnabled && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6993
6994
  "div",
6994
6995
  {
6995
6996
  "data-ohw-item-drag-surface": "",
6996
- className: "pointer-events-auto absolute inset-0 cursor-text rounded-lg",
6997
+ className: "pointer-events-auto absolute inset-0 cursor-text",
6998
+ style: { borderRadius: CHROME_RADIUS },
6997
6999
  onPointerDown: (e) => {
6998
7000
  if (e.button !== 0) return;
6999
7001
  onItemPointerDown?.(e);
@@ -7745,6 +7747,15 @@ var SKELETON_CSS = `
7745
7747
  animation: ohw-media-shimmer 1.4s infinite;
7746
7748
  }
7747
7749
  `;
7750
+ var REPLACE_BUTTON_INSET = 12;
7751
+ function replaceButtonMode(image, button) {
7752
+ if (!button.fullWidth || !button.height) return "none";
7753
+ const room = REPLACE_BUTTON_INSET * 2;
7754
+ if (image.height < button.height + room) return "none";
7755
+ if (image.width >= button.fullWidth + room) return "full";
7756
+ if (image.width >= button.height + room) return "icon";
7757
+ return "none";
7758
+ }
7748
7759
  function MediaOverlay({
7749
7760
  hover,
7750
7761
  isUploading,
@@ -7762,6 +7773,19 @@ function MediaOverlay({
7762
7773
  const showChrome = !selected || hovered;
7763
7774
  const autoplay = hover.videoAutoplay ?? true;
7764
7775
  const muted = hover.videoMuted ?? true;
7776
+ const probeRef = React8.useRef(null);
7777
+ const [buttonSize, setButtonSize] = React8.useState(null);
7778
+ React8.useLayoutEffect(() => {
7779
+ const probe = probeRef.current;
7780
+ if (!probe) return;
7781
+ const { width, height } = probe.getBoundingClientRect();
7782
+ if (!width || !height) return;
7783
+ setButtonSize(
7784
+ (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
7785
+ );
7786
+ }, [isVideo]);
7787
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
7788
+ const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
7765
7789
  const box = {
7766
7790
  position: "fixed",
7767
7791
  top: rect.top,
@@ -7851,7 +7875,7 @@ function MediaOverlay({
7851
7875
  ) : null;
7852
7876
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
7853
7877
  settingsBar,
7854
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7878
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7855
7879
  "div",
7856
7880
  {
7857
7881
  "data-ohw-bridge": "",
@@ -7870,29 +7894,64 @@ function MediaOverlay({
7870
7894
  background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7871
7895
  },
7872
7896
  onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
7873
- children: showChrome && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7874
- Button,
7875
- {
7876
- "data-ohw-media-overlay": "",
7877
- variant: "outline",
7878
- size: "sm",
7879
- className: "gap-1.5 cursor-pointer hover:bg-background",
7880
- style: {
7881
- ...OVERLAY_BUTTON_STYLE,
7882
- // Don't let Replace steal clicks when text (e.g. nav) sits on top of this media.
7883
- pointerEvents: hover.hasTextOverlap ? "none" : "auto"
7884
- },
7885
- onMouseDown: (e) => e.preventDefault(),
7886
- onClick: (e) => {
7887
- e.stopPropagation();
7888
- onReplace(hover.key);
7889
- },
7890
- children: [
7891
- isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7892
- isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image"
7893
- ]
7894
- }
7895
- )
7897
+ children: [
7898
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7899
+ Button,
7900
+ {
7901
+ ref: probeRef,
7902
+ "aria-hidden": true,
7903
+ tabIndex: -1,
7904
+ variant: "outline",
7905
+ size: "sm",
7906
+ className: "gap-1.5",
7907
+ style: {
7908
+ ...OVERLAY_BUTTON_STYLE,
7909
+ position: "fixed",
7910
+ top: 0,
7911
+ left: 0,
7912
+ visibility: "hidden",
7913
+ pointerEvents: "none"
7914
+ },
7915
+ children: [
7916
+ isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7917
+ replaceLabel
7918
+ ]
7919
+ }
7920
+ ),
7921
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7922
+ Button,
7923
+ {
7924
+ "data-ohw-media-overlay": "",
7925
+ variant: "outline",
7926
+ size: "sm",
7927
+ "aria-label": replaceLabel,
7928
+ className: "gap-1.5 cursor-pointer hover:bg-background",
7929
+ style: {
7930
+ ...OVERLAY_BUTTON_STYLE,
7931
+ // Icon-only is a square of the same height, so the pair can never differ vertically.
7932
+ // `minWidth` has to go too: the button's own size carries `min-w-[64px]`, and a
7933
+ // minimum beats a width — the square came out wider than the avatar it sat on.
7934
+ ...replaceMode === "icon" && buttonSize ? {
7935
+ width: buttonSize.height,
7936
+ minWidth: buttonSize.height,
7937
+ padding: 0,
7938
+ justifyContent: "center"
7939
+ } : null,
7940
+ // Don't let Replace steal clicks when text (e.g. nav) sits on top of this media.
7941
+ pointerEvents: hover.hasTextOverlap ? "none" : "auto"
7942
+ },
7943
+ onMouseDown: (e) => e.preventDefault(),
7944
+ onClick: (e) => {
7945
+ e.stopPropagation();
7946
+ onReplace(hover.key);
7947
+ },
7948
+ children: [
7949
+ isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7950
+ replaceMode === "full" ? replaceLabel : null
7951
+ ]
7952
+ }
7953
+ )
7954
+ ]
7896
7955
  }
7897
7956
  )
7898
7957
  ] });
@@ -15556,7 +15615,8 @@ function EditGlowChrome({
15556
15615
  position: "absolute",
15557
15616
  inset: 0,
15558
15617
  border: "2px solid var(--ohw-primary, #0885FE)",
15559
- borderRadius: 8,
15618
+ // Same radius as every other interaction frame — this one was drawing 8 (OHH-701/722).
15619
+ borderRadius: CHROME_RADIUS,
15560
15620
  boxShadow: "0 0 0 4px color-mix(in srgb, var(--ohw-primary, #0885FE) 12%, transparent)",
15561
15621
  pointerEvents: "none"
15562
15622
  }