@ohhwells/bridge 0.1.66-next.192 → 0.1.67-next.193

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
@@ -7551,13 +7551,17 @@ function MediaOverlay({
7551
7551
  hover,
7552
7552
  isUploading,
7553
7553
  fadingOut = false,
7554
+ selected = false,
7555
+ hovered = false,
7554
7556
  onFadeOutComplete,
7555
7557
  onReplace,
7558
+ onSelect,
7556
7559
  onVideoSettingsChange
7557
7560
  }) {
7558
7561
  const { rect } = hover;
7559
7562
  const skeletonRef = React7.useRef(null);
7560
7563
  const isVideo = hover.elementType === "video";
7564
+ const showChrome = !selected || hovered;
7561
7565
  const autoplay = hover.videoAutoplay ?? true;
7562
7566
  const muted = hover.videoMuted ?? true;
7563
7567
  const box = {
@@ -7592,7 +7596,7 @@ function MediaOverlay({
7592
7596
  }
7593
7597
  );
7594
7598
  }
7595
- const settingsBar = isVideo && !hover.isDragOver ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7599
+ const settingsBar = isVideo && !hover.isDragOver && showChrome ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7596
7600
  "div",
7597
7601
  {
7598
7602
  "data-ohw-bridge": "",
@@ -7662,11 +7666,13 @@ function MediaOverlay({
7662
7666
  // in-document, pointer-events does it natively. The button below opts back in, so
7663
7667
  // Replace still works.
7664
7668
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
7665
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
7666
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7669
+ // Selected: a firm component ring with no wash, so the image reads as chosen rather
7670
+ // than hovered. Hover keeps the existing tinted preview.
7671
+ boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
7672
+ background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
7667
7673
  },
7668
- onClick: () => onReplace(hover.key),
7669
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7674
+ onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
7675
+ children: showChrome && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7670
7676
  Button,
7671
7677
  {
7672
7678
  "data-ohw-media-overlay": "",
@@ -15587,6 +15593,70 @@ function OhhwellsBridge() {
15587
15593
  const hoveredImageHasTextOverlapRef = (0, import_react17.useRef)(false);
15588
15594
  const dragOverElRef = (0, import_react17.useRef)(null);
15589
15595
  const [mediaHover, setMediaHover] = (0, import_react17.useState)(null);
15596
+ const [selectedMedia, setSelectedMedia] = (0, import_react17.useState)(null);
15597
+ const selectedMediaElRef = (0, import_react17.useRef)(null);
15598
+ const clearMediaSelection = (0, import_react17.useCallback)(() => {
15599
+ const prev = selectedMediaElRef.current;
15600
+ selectedMediaElRef.current = null;
15601
+ setSelectedMedia(null);
15602
+ const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
15603
+ if (sectionEl) {
15604
+ postToParentRef.current({
15605
+ type: "ow:section-selected",
15606
+ sectionId: sectionEl.dataset.ohwSection ?? null,
15607
+ sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
15608
+ key: null
15609
+ });
15610
+ }
15611
+ }, []);
15612
+ const clearMediaSelectionRef = (0, import_react17.useRef)(clearMediaSelection);
15613
+ clearMediaSelectionRef.current = clearMediaSelection;
15614
+ const selectMediaElement = (0, import_react17.useCallback)((el) => {
15615
+ const r2 = el.getBoundingClientRect();
15616
+ const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
15617
+ selectedMediaElRef.current = el;
15618
+ setSelectedMedia({
15619
+ key: el.dataset.ohwKey ?? "",
15620
+ rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height },
15621
+ elementType: el.dataset.ohwEditable ?? "image",
15622
+ hasTextOverlap: false,
15623
+ isDragOver: false,
15624
+ ...video ? { videoAutoplay: video.autoplay, videoMuted: video.muted } : {}
15625
+ });
15626
+ const sectionEl = el.closest("[data-ohw-section]");
15627
+ aiSectionApiRef.current?.selectFromElement(el, { report: false });
15628
+ postToParentRef.current({
15629
+ type: "ow:section-selected",
15630
+ sectionId: sectionEl?.dataset.ohwSection ?? null,
15631
+ sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
15632
+ key: el.dataset.ohwKey ?? null,
15633
+ // Display name for the pill — the raw key prettifies into fragments ("Img"); the
15634
+ // bridge knows what the node IS, so it names it.
15635
+ keyLabel: el.dataset.ohwEditable === "video" ? "Video" : "Image"
15636
+ });
15637
+ }, []);
15638
+ const selectMediaElementRef = (0, import_react17.useRef)(selectMediaElement);
15639
+ selectMediaElementRef.current = selectMediaElement;
15640
+ (0, import_react17.useEffect)(() => {
15641
+ if (!selectedMedia) return;
15642
+ const update = () => {
15643
+ const el = selectedMediaElRef.current;
15644
+ if (!el || !el.isConnected) {
15645
+ clearMediaSelection();
15646
+ return;
15647
+ }
15648
+ const r2 = el.getBoundingClientRect();
15649
+ setSelectedMedia(
15650
+ (prev) => prev ? { ...prev, rect: { top: r2.top, left: r2.left, width: r2.width, height: r2.height } } : prev
15651
+ );
15652
+ };
15653
+ window.addEventListener("scroll", update, true);
15654
+ window.addEventListener("resize", update);
15655
+ return () => {
15656
+ window.removeEventListener("scroll", update, true);
15657
+ window.removeEventListener("resize", update);
15658
+ };
15659
+ }, [selectedMedia !== null]);
15590
15660
  const [carouselHover, setCarouselHover] = (0, import_react17.useState)(null);
15591
15661
  const [uploadingRects, setUploadingRects] = (0, import_react17.useState)({});
15592
15662
  const hoveredGapRef = (0, import_react17.useRef)(null);
@@ -17719,6 +17789,9 @@ function OhhwellsBridge() {
17719
17789
  )) {
17720
17790
  return;
17721
17791
  }
17792
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
17793
+ clearMediaSelectionRef.current();
17794
+ }
17722
17795
  {
17723
17796
  const formEl = getFormElement(target);
17724
17797
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -17866,8 +17939,11 @@ function OhhwellsBridge() {
17866
17939
  if (isMediaEditable(editable) && !buttonOnMedia) {
17867
17940
  e.preventDefault();
17868
17941
  e.stopPropagation();
17869
- aiSectionApiRef.current?.selectFromElement(editable);
17870
- postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
17942
+ if (selectedMediaElRef.current === editable) {
17943
+ postToParentRef.current({ type: "ow:image-pick", key: editable.dataset.ohwKey ?? "", elementType: editable.dataset.ohwEditable ?? "image" });
17944
+ } else {
17945
+ selectMediaElementRef.current(editable);
17946
+ }
17871
17947
  return;
17872
17948
  }
17873
17949
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
@@ -19363,6 +19439,10 @@ function OhhwellsBridge() {
19363
19439
  const handleKeyDown = (e) => {
19364
19440
  if (e.key === "Escape" && document.querySelector("[data-ohw-section-picker]")) return;
19365
19441
  if (e.key === "Escape" && document.querySelector("[data-ohw-more-menu]")) return;
19442
+ if (e.key === "Escape" && selectedMediaElRef.current) {
19443
+ clearMediaSelectionRef.current();
19444
+ return;
19445
+ }
19366
19446
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
19367
19447
  e.preventDefault();
19368
19448
  selectAllTextInEditable(activeElRef.current);
@@ -20147,7 +20227,7 @@ function OhhwellsBridge() {
20147
20227
  postToParent2({
20148
20228
  type: "ow:ready",
20149
20229
  version: "1",
20150
- bridgeVersion: "0.1.66",
20230
+ bridgeVersion: "0.1.67",
20151
20231
  path: pathname,
20152
20232
  nodes: collectEditableNodes(editContentRef.current),
20153
20233
  sections
@@ -20499,11 +20579,22 @@ function OhhwellsBridge() {
20499
20579
  const showEditLink = toolbarShowEditLink;
20500
20580
  const currentSections = sectionsByPath[pathname] ?? [];
20501
20581
  linkPopoverOpenRef.current = linkPopover !== null;
20582
+ const handleMediaSelect = (0, import_react17.useCallback)((key) => {
20583
+ const el = hoveredImageRef.current?.dataset.ohwKey === key ? hoveredImageRef.current : Array.from(document.querySelectorAll(MEDIA_SELECTOR)).find(
20584
+ (m) => (m.dataset.ohwKey ?? "") === key
20585
+ ) ?? null;
20586
+ if (!el) return;
20587
+ selectMediaElementRef.current(el);
20588
+ }, []);
20502
20589
  const handleMediaReplace = (0, import_react17.useCallback)(
20503
20590
  (key) => {
20504
- postToParent2({ type: "ow:image-pick", key, elementType: mediaHover?.elementType ?? "image" });
20591
+ postToParent2({
20592
+ type: "ow:image-pick",
20593
+ key,
20594
+ elementType: mediaHover?.elementType ?? selectedMedia?.elementType ?? "image"
20595
+ });
20505
20596
  },
20506
- [postToParent2, mediaHover?.elementType]
20597
+ [postToParent2, mediaHover?.elementType, selectedMedia?.elementType]
20507
20598
  );
20508
20599
  const handleEditCarousel = (0, import_react17.useCallback)(
20509
20600
  (key) => {
@@ -20575,12 +20666,25 @@ function OhhwellsBridge() {
20575
20666
  },
20576
20667
  `uploading-${key}`
20577
20668
  )),
20578
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20669
+ mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20579
20670
  MediaOverlay,
20580
20671
  {
20581
20672
  hover: mediaHover,
20582
20673
  isUploading: false,
20583
20674
  onReplace: handleMediaReplace,
20675
+ onSelect: handleMediaSelect,
20676
+ onVideoSettingsChange: handleVideoSettingsChange
20677
+ }
20678
+ ),
20679
+ selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
20680
+ MediaOverlay,
20681
+ {
20682
+ hover: selectedMedia,
20683
+ selected: true,
20684
+ hovered: mediaHover?.key === selectedMedia.key,
20685
+ isUploading: false,
20686
+ onReplace: handleMediaReplace,
20687
+ onSelect: handleMediaSelect,
20584
20688
  onVideoSettingsChange: handleVideoSettingsChange
20585
20689
  }
20586
20690
  ),