@ohhwells/bridge 0.1.114-next.306 → 0.1.114-next.309

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
@@ -15346,6 +15346,30 @@ function relativeLuminance(color) {
15346
15346
  };
15347
15347
  return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
15348
15348
  }
15349
+ function contrastRatio(a, b) {
15350
+ const lighter = Math.max(relativeLuminance(a), relativeLuminance(b));
15351
+ const darker = Math.min(relativeLuminance(a), relativeLuminance(b));
15352
+ return (lighter + 0.05) / (darker + 0.05);
15353
+ }
15354
+ function compositeOver(fg, bg) {
15355
+ const a = fg.a + bg.a * (1 - fg.a);
15356
+ if (a < 1e-3) return { ...bg, a: 0 };
15357
+ return {
15358
+ r: (fg.r * fg.a + bg.r * bg.a * (1 - fg.a)) / a,
15359
+ g: (fg.g * fg.a + bg.g * bg.a * (1 - fg.a)) / a,
15360
+ b: (fg.b * fg.a + bg.b * bg.a * (1 - fg.a)) / a,
15361
+ a
15362
+ };
15363
+ }
15364
+ function previewSlotForChrome(chrome, slotBackground) {
15365
+ const text = parseColor(chrome.color);
15366
+ if (!text || text.a < 0.2) return slotBackground;
15367
+ const slot = parseColor(slotBackground) ?? { r: 250, g: 250, b: 249, a: 1 };
15368
+ const fill = parseColor(chrome.background);
15369
+ const effective = fill && !isTransparent(fill) ? compositeOver(fill, slot) : slot;
15370
+ if (contrastRatio(text, effective) >= 3) return slotBackground;
15371
+ return relativeLuminance(text) > 0.55 ? "#2c2c2c" : "#f4f4f5";
15372
+ }
15349
15373
  function parseColor(input) {
15350
15374
  if (!input) return null;
15351
15375
  const value = input.trim().toLowerCase();
@@ -15422,10 +15446,10 @@ function declaredVariant(className) {
15422
15446
  return match && isButtonVariantId(match[1]) ? match[1] : null;
15423
15447
  }
15424
15448
  function fillSlot(sample) {
15425
- if (hasOpaqueBackground(sample.background)) return "primary";
15426
15449
  const width = parseFloat(sample.borderWidth);
15427
15450
  const border = parseColor(sample.borderColor);
15428
15451
  if (width > 0 && !isTransparent(border)) return "secondary";
15452
+ if (hasOpaqueBackground(sample.background)) return "primary";
15429
15453
  return "ghost";
15430
15454
  }
15431
15455
  function sampleSlot(sample) {
@@ -15449,6 +15473,15 @@ function paintColor(chrome) {
15449
15473
  const text = parseColor(chrome.color);
15450
15474
  return text ? toCss({ ...text, a: 1 }) : chrome.color;
15451
15475
  }
15476
+ function accentColor(chrome) {
15477
+ const bg = parseColor(chrome.background);
15478
+ if (bg && !isTransparent(bg) && relativeLuminance(bg) < 0.55) {
15479
+ return toCss({ ...bg, a: 1 });
15480
+ }
15481
+ const text = parseColor(chrome.color);
15482
+ if (text && !isTransparent(text)) return toCss({ ...text, a: 1 });
15483
+ return paintColor(chrome);
15484
+ }
15452
15485
  function chromeFrom(sample) {
15453
15486
  return {
15454
15487
  background: sample.background || TRANSPARENT,
@@ -15468,7 +15501,7 @@ function filledPrimary(seed) {
15468
15501
  };
15469
15502
  }
15470
15503
  function generatedSecondary(primary) {
15471
- const paint = paintColor(primary);
15504
+ const paint = accentColor(primary);
15472
15505
  return {
15473
15506
  background: TRANSPARENT,
15474
15507
  color: paint,
@@ -15479,13 +15512,13 @@ function generatedSecondary(primary) {
15479
15512
  function generatedGhost(primary) {
15480
15513
  return {
15481
15514
  background: TRANSPARENT,
15482
- color: paintColor(primary),
15515
+ color: accentColor(primary),
15483
15516
  borderColor: TRANSPARENT,
15484
15517
  borderWidth: "0px"
15485
15518
  };
15486
15519
  }
15487
15520
  function normalizeOutlineChrome(outline, primary) {
15488
- const paint = paintColor(primary);
15521
+ const paint = accentColor(primary);
15489
15522
  const text = parseColor(outline.color);
15490
15523
  const paintRgb = parseColor(paint);
15491
15524
  const primaryLabel = parseColor(primary.color);
@@ -15721,36 +15754,49 @@ function readSizeSample(el) {
15721
15754
  gap: parsePx(style.columnGap || style.gap, rootFont)
15722
15755
  };
15723
15756
  }
15757
+ function readTypography(el) {
15758
+ const style = getComputedStyle(el);
15759
+ return {
15760
+ fontFamily: style.fontFamily,
15761
+ fontWeight: style.fontWeight,
15762
+ letterSpacing: style.letterSpacing,
15763
+ textTransform: style.textTransform,
15764
+ textDecoration: style.textDecorationLine || style.textDecoration || "none"
15765
+ };
15766
+ }
15724
15767
  var buttonOriginByEl = /* @__PURE__ */ new WeakMap();
15725
- function hasEditorPaint(el) {
15726
- return el.hasAttribute("data-ohw-btn-variant") || el.hasAttribute("data-ohw-btn-size") || Boolean(el.style.background || el.style.padding || el.style.height);
15768
+ var ORIGIN_ATTR = "data-ohw-btn-origin";
15769
+ function parseOriginAttr(raw) {
15770
+ if (!raw) return null;
15771
+ try {
15772
+ const value = JSON.parse(raw);
15773
+ if (!value?.variant || !value.size || !value.typography) return null;
15774
+ return value;
15775
+ } catch {
15776
+ return null;
15777
+ }
15727
15778
  }
15728
- function measureSourceOrigin(el) {
15729
- const parent = el.parentElement;
15730
- if (!parent) {
15731
- return { variant: readVariantSample(el), size: readSizeSample(el) };
15732
- }
15733
- const probe = el.cloneNode(true);
15734
- probe.removeAttribute("style");
15735
- probe.removeAttribute("data-ohw-btn-variant");
15736
- probe.removeAttribute("data-ohw-btn-size");
15737
- probe.removeAttribute("data-ohw-selected");
15738
- probe.setAttribute("data-ohw-btn-probe", "");
15739
- for (const node of probe.querySelectorAll("[style]")) {
15740
- node.removeAttribute("style");
15741
- }
15742
- probe.style.cssText = "position:absolute;left:-99999px;top:0;visibility:hidden;pointer-events:none;display:inline-flex;";
15743
- parent.insertBefore(probe, el);
15744
- void probe.offsetHeight;
15745
- const origin = { variant: readVariantSample(probe), size: readSizeSample(probe) };
15746
- probe.remove();
15747
- return origin;
15779
+ function measureLiveOrigin(el) {
15780
+ return {
15781
+ variant: readVariantSample(el),
15782
+ size: readSizeSample(el),
15783
+ typography: readTypography(el)
15784
+ };
15748
15785
  }
15749
15786
  function captureButtonOrigin(el) {
15750
15787
  const cached = buttonOriginByEl.get(el);
15751
15788
  if (cached) return cached;
15752
- const origin = hasEditorPaint(el) ? measureSourceOrigin(el) : { variant: readVariantSample(el), size: readSizeSample(el) };
15789
+ const fromAttr = parseOriginAttr(el.getAttribute(ORIGIN_ATTR));
15790
+ if (fromAttr) {
15791
+ buttonOriginByEl.set(el, fromAttr);
15792
+ return fromAttr;
15793
+ }
15794
+ const origin = measureLiveOrigin(el);
15753
15795
  buttonOriginByEl.set(el, origin);
15796
+ try {
15797
+ el.setAttribute(ORIGIN_ATTR, JSON.stringify(origin));
15798
+ } catch {
15799
+ }
15754
15800
  return origin;
15755
15801
  }
15756
15802
  function listStyleButtons(doc) {
@@ -15846,9 +15892,11 @@ function labelElement(el) {
15846
15892
  return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? (el.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ? el : null);
15847
15893
  }
15848
15894
  function applyButtonStyle(el, assignment, tokens) {
15849
- captureButtonOrigin(el);
15895
+ const origin = captureButtonOrigin(el);
15850
15896
  const chrome = tokens.variants[assignment.variant];
15851
- const geometry = tokens.sizes[assignment.size];
15897
+ const originSlot = classifyHeight(origin.size.height);
15898
+ const geometry = assignment.size === originSlot ? geometryFromSample({ ...origin.size, borderRadius: origin.size.borderRadius, gap: origin.size.gap }) : tokens.sizes[assignment.size];
15899
+ const { typography } = origin;
15852
15900
  el.setAttribute("data-ohw-btn-variant", assignment.variant);
15853
15901
  el.setAttribute("data-ohw-btn-size", assignment.size);
15854
15902
  el.style.boxSizing = "border-box";
@@ -15862,9 +15910,18 @@ function applyButtonStyle(el, assignment, tokens) {
15862
15910
  el.style.borderWidth = chrome.borderWidth;
15863
15911
  el.style.padding = `${geometry.paddingTop} ${geometry.paddingRight} ${geometry.paddingBottom} ${geometry.paddingLeft}`;
15864
15912
  el.style.fontSize = geometry.fontSize;
15865
- el.style.borderRadius = geometry.borderRadius;
15913
+ el.style.borderRadius = px(origin.size.borderRadius);
15866
15914
  el.style.gap = geometry.gap;
15867
15915
  el.style.height = px(geometry.height);
15916
+ el.style.fontFamily = typography.fontFamily;
15917
+ el.style.fontWeight = typography.fontWeight;
15918
+ el.style.letterSpacing = typography.letterSpacing;
15919
+ el.style.textTransform = typography.textTransform;
15920
+ if (typography.textDecoration && typography.textDecoration !== "none") {
15921
+ el.style.textDecoration = typography.textDecoration;
15922
+ } else {
15923
+ el.style.textDecoration = "none";
15924
+ }
15868
15925
  const label = labelElement(el);
15869
15926
  if (label && label !== el) label.style.color = chrome.color;
15870
15927
  }
@@ -15902,6 +15959,7 @@ function ButtonStylePanel({
15902
15959
  const chrome = variants[id];
15903
15960
  const selected = id === variant;
15904
15961
  const hasBorder = parseFloat(chrome.borderWidth) > 0;
15962
+ const cardSlot = previewSlotForChrome(chrome, slotBackground);
15905
15963
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
15906
15964
  "button",
15907
15965
  {
@@ -15922,7 +15980,7 @@ function ButtonStylePanel({
15922
15980
  "div",
15923
15981
  {
15924
15982
  className: "flex w-full items-center justify-center py-5",
15925
- style: { background: slotBackground },
15983
+ style: { background: cardSlot },
15926
15984
  children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
15927
15985
  "span",
15928
15986
  {
@@ -17621,7 +17679,14 @@ function collectEditableNodes(extraContent, root = document) {
17621
17679
  // so collecting the DOM alone left them behind: the draft knew the row was showing icons and
17622
17680
  // had gained an item, and the published page went back to the template's own (OHH-736).
17623
17681
  SOCIALS_ORDER_KEY,
17624
- SOCIALS_DISPLAY_KEY
17682
+ SOCIALS_DISPLAY_KEY,
17683
+ // Button variant/size tokens and per-CTA assignments — JSON in editContent, applied on hydrate.
17684
+ BUTTON_VARIANTS_KEY,
17685
+ BUTTON_SIZES_KEY,
17686
+ BUTTON_ASSIGNMENTS_KEY,
17687
+ BUTTON_COLLECTIONS_KEY,
17688
+ BUTTON_SNAPSHOTS_KEY,
17689
+ BUTTON_HIDDEN_KEY
17625
17690
  ]) {
17626
17691
  const text = extraContent[key];
17627
17692
  if (typeof text === "string" && text.length > 0) {
@@ -23132,7 +23197,13 @@ function OhhwellsBridge() {
23132
23197
  setHoveredItemRect(null);
23133
23198
  }
23134
23199
  };
23200
+ const syncSelectedMediaPointerRelease = (released) => {
23201
+ setSelectedMedia(
23202
+ (prev) => prev && prev.hasTextOverlap !== released ? { ...prev, hasTextOverlap: released } : prev
23203
+ );
23204
+ };
23135
23205
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
23206
+ syncSelectedMediaPointerRelease(true);
23136
23207
  if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
23137
23208
  if (hoveredImageRef.current) {
23138
23209
  hoveredImageRef.current = null;
@@ -23327,6 +23398,7 @@ function OhhwellsBridge() {
23327
23398
  return;
23328
23399
  }
23329
23400
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
23401
+ syncSelectedMediaPointerRelease(false);
23330
23402
  if (imgEl !== hoveredImageRef.current || hoveredImageHasTextOverlapRef.current) {
23331
23403
  hoveredImageRef.current = imgEl;
23332
23404
  showImageHover(imgEl, isDragOver);