@ohhwells/bridge 0.1.139-next.385 → 0.1.139-next.388

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
@@ -1522,6 +1522,52 @@ function assignmentFor(el, content, tokens) {
1522
1522
  function labelElement(el) {
1523
1523
  return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? (el.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ? el : null);
1524
1524
  }
1525
+ var BRAND_TRACK_PRECEDENCE = {
1526
+ background: ["primary", "accent", "background", "surface", "on-primary", "text", "border", "text-muted"],
1527
+ color: ["on-primary", "text", "primary", "accent", "text-muted", "background", "surface", "border"],
1528
+ borderColor: ["border", "primary", "accent", "text", "on-primary", "background", "surface", "text-muted"]
1529
+ };
1530
+ var FONT_TRACK_VARS = ["--font-body", "--font-heading", "--font-display", "--brand-font-body", "--brand-font-heading"];
1531
+ function readRootVar(doc, name) {
1532
+ const root = doc.documentElement;
1533
+ return root.style.getPropertyValue(name).trim() || getComputedStyle(root).getPropertyValue(name).trim();
1534
+ }
1535
+ function sameColor(a, b) {
1536
+ return Math.round(a.r) === Math.round(b.r) && Math.round(a.g) === Math.round(b.g) && Math.round(a.b) === Math.round(b.b) && Math.abs(a.a - b.a) < 0.02;
1537
+ }
1538
+ function brandTrackedColor(doc, color, roles) {
1539
+ if (!color || color.includes("var(")) return color;
1540
+ const parsed = parseColor(color);
1541
+ if (!parsed || isTransparent(parsed)) return color;
1542
+ for (const role of roles) {
1543
+ const name = `--brand-${role}`;
1544
+ const token = parseColor(readRootVar(doc, name));
1545
+ if (token && !isTransparent(token) && sameColor(parsed, token)) return `var(${name}, ${color})`;
1546
+ }
1547
+ return color;
1548
+ }
1549
+ function brandTrackedChrome(doc, chrome) {
1550
+ return {
1551
+ ...chrome,
1552
+ background: brandTrackedColor(doc, chrome.background, BRAND_TRACK_PRECEDENCE.background),
1553
+ color: brandTrackedColor(doc, chrome.color, BRAND_TRACK_PRECEDENCE.color),
1554
+ borderColor: brandTrackedColor(doc, chrome.borderColor, BRAND_TRACK_PRECEDENCE.borderColor)
1555
+ };
1556
+ }
1557
+ function firstFamily(stack) {
1558
+ const first = stack.split(",")[0]?.trim() ?? "";
1559
+ return first.replace(/^['"]|['"]$/g, "").toLowerCase();
1560
+ }
1561
+ function brandTrackedFontFamily(doc, stack) {
1562
+ if (!stack || stack.includes("var(")) return stack;
1563
+ const family = firstFamily(stack);
1564
+ if (!family) return stack;
1565
+ for (const name of FONT_TRACK_VARS) {
1566
+ const raw = readRootVar(doc, name);
1567
+ if (raw && firstFamily(raw) === family) return `var(${name}, ${stack})`;
1568
+ }
1569
+ return stack;
1570
+ }
1525
1571
  var paintLockByEl = /* @__PURE__ */ new WeakMap();
1526
1572
  var paintObserverByEl = /* @__PURE__ */ new WeakMap();
1527
1573
  var paintingButton = false;
@@ -1548,7 +1594,7 @@ function paintButtonChrome(el, assignment, tokens) {
1548
1594
  el.style.borderRadius = px(origin.size.borderRadius);
1549
1595
  el.style.gap = geometry.gap;
1550
1596
  el.style.height = px(geometry.height);
1551
- el.style.fontFamily = typography.fontFamily;
1597
+ el.style.fontFamily = brandTrackedFontFamily(el.ownerDocument, typography.fontFamily);
1552
1598
  el.style.fontWeight = typography.fontWeight;
1553
1599
  el.style.letterSpacing = typography.letterSpacing;
1554
1600
  el.style.textTransform = typography.textTransform;
@@ -1577,9 +1623,18 @@ function paintedTokensOf(el) {
1577
1623
  return paintLockByEl.get(el)?.tokens ?? null;
1578
1624
  }
1579
1625
  function applyButtonStyle(el, assignment, tokens) {
1580
- paintLockByEl.set(el, { assignment, tokens });
1626
+ const doc = el.ownerDocument;
1627
+ const tracked = {
1628
+ ...tokens,
1629
+ variants: {
1630
+ primary: brandTrackedChrome(doc, tokens.variants.primary),
1631
+ secondary: brandTrackedChrome(doc, tokens.variants.secondary),
1632
+ ghost: brandTrackedChrome(doc, tokens.variants.ghost)
1633
+ }
1634
+ };
1635
+ paintLockByEl.set(el, { assignment, tokens: tracked });
1581
1636
  paintingButton = true;
1582
- paintButtonChrome(el, assignment, tokens);
1637
+ paintButtonChrome(el, assignment, tracked);
1583
1638
  paintingButton = false;
1584
1639
  guardTemplateHover(el);
1585
1640
  }
@@ -2479,6 +2534,49 @@ function socialHrefKey(item) {
2479
2534
  function socialIconKey(item) {
2480
2535
  return item.querySelector(ICON_SELECTOR)?.dataset.ohwKey ?? null;
2481
2536
  }
2537
+ var SOCIAL_PLATFORM_LABELS = {
2538
+ instagram: "Instagram",
2539
+ facebook: "Facebook",
2540
+ x: "X",
2541
+ twitter: "X",
2542
+ tiktok: "TikTok",
2543
+ youtube: "YouTube",
2544
+ linkedin: "LinkedIn",
2545
+ pinterest: "Pinterest",
2546
+ whatsapp: "WhatsApp",
2547
+ telegram: "Telegram",
2548
+ spotify: "Spotify",
2549
+ threads: "Threads",
2550
+ vimeo: "Vimeo",
2551
+ discord: "Discord",
2552
+ medium: "Medium",
2553
+ website: "Website"
2554
+ };
2555
+ function socialPlatformLabel(item, content) {
2556
+ const iconKey = socialIconKey(item);
2557
+ const chosen = iconKey ? (content[socialPlatformKey(iconKey)] ?? "").trim().toLowerCase() : "";
2558
+ if (chosen) return SOCIAL_PLATFORM_LABELS[chosen] ?? chosen.charAt(0).toUpperCase() + chosen.slice(1);
2559
+ const label = socialLabelElement(item);
2560
+ const words = label && !label.hasAttribute(SOCIALS_EMPTY_LABEL_ATTR) ? (label.textContent ?? "").replace(/\s+/g, " ").trim() : "";
2561
+ if (words) return words;
2562
+ const link = item.matches("a") ? item : item.querySelector("a");
2563
+ const aria = (link?.getAttribute("aria-label") ?? item.getAttribute("aria-label") ?? "").trim();
2564
+ if (aria) return aria;
2565
+ const href = (link?.getAttribute("href") ?? "").trim();
2566
+ if (/^https?:\/\//i.test(href)) {
2567
+ let host = "";
2568
+ try {
2569
+ host = new URL(href).hostname.replace(/^www\./i, "").toLowerCase();
2570
+ } catch {
2571
+ host = "";
2572
+ }
2573
+ if (host.includes("x.com") || host.includes("twitter.com")) return "X";
2574
+ for (const [id, name] of Object.entries(SOCIAL_PLATFORM_LABELS)) {
2575
+ if (host.includes(id)) return name;
2576
+ }
2577
+ }
2578
+ return "Social";
2579
+ }
2482
2580
  var SOCIALS_ORDER_KEY = "__ohw_socials_order";
2483
2581
  function fromMarkup(markup) {
2484
2582
  const holder = document.createElement("div");
@@ -7060,8 +7158,8 @@ function applyBrandToDom(kit) {
7060
7158
  if (kit.fonts) {
7061
7159
  appliedFonts = applyFonts ? kit.fonts : null;
7062
7160
  }
7161
+ for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
7063
7162
  if (kit.buttons) {
7064
- for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
7065
7163
  for (const [name, value] of Object.entries(buttonStyleVars({ ...kit, buttons: kit.buttons }))) root.style.setProperty(name, value);
7066
7164
  }
7067
7165
  syncButtonRules(kit.buttons ? kit : null);
@@ -10491,7 +10589,7 @@ function isChromeSection2(el) {
10491
10589
  if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) return true;
10492
10590
  return el.tagName === "HEADER" || el.tagName === "FOOTER";
10493
10591
  }
10494
- function readRootVar(name) {
10592
+ function readRootVar2(name) {
10495
10593
  if (typeof document === "undefined") return "";
10496
10594
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
10497
10595
  }
@@ -10531,14 +10629,14 @@ function humanizeFontFamily(name) {
10531
10629
  return name.replace(/^__/, "").replace(/_[0-9a-f]{6}$/i, "").replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim().replace(/\s+/g, " ").replace(/(^|\s)([a-z])/g, (_, sep, c) => sep + c.toUpperCase());
10532
10630
  }
10533
10631
  function readFontVar(name) {
10534
- return humanizeFontFamily(primaryFontFamily(readRootVar(name)));
10632
+ return humanizeFontFamily(primaryFontFamily(readRootVar2(name)));
10535
10633
  }
10536
10634
  function deriveBrandOverride() {
10537
- const dark = readRootVar("--ohw-brand-dark");
10538
- const primary = readRootVar("--ohw-brand-primary");
10539
- const light = readRootVar("--ohw-brand-light");
10635
+ const dark = readRootVar2("--ohw-brand-dark");
10636
+ const primary = readRootVar2("--ohw-brand-primary");
10637
+ const light = readRootVar2("--ohw-brand-light");
10540
10638
  if (!dark || !primary || !light) return null;
10541
- const accent = readRootVar("--ohw-brand-accent");
10639
+ const accent = readRootVar2("--ohw-brand-accent");
10542
10640
  const heading = readFontVar("--font-heading") || readFontVar("--font-display");
10543
10641
  const body = readFontVar("--font-body");
10544
10642
  return {
@@ -10550,11 +10648,11 @@ function deriveBrandOverride() {
10550
10648
  };
10551
10649
  }
10552
10650
  function deriveTemplateBrandLive() {
10553
- const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
10554
- const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
10555
- const light = readRootVar("--brand-background") || readRootVar("--color-light");
10651
+ const primary = readRootVar2("--brand-primary") || readRootVar2("--color-primary");
10652
+ const dark = readRootVar2("--brand-text") || readRootVar2("--color-dark");
10653
+ const light = readRootVar2("--brand-background") || readRootVar2("--color-light");
10556
10654
  if (!dark || !primary || !light) return null;
10557
- const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
10655
+ const accent = readRootVar2("--brand-accent") || readRootVar2("--color-accent");
10558
10656
  const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
10559
10657
  const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
10560
10658
  return {
@@ -25172,36 +25270,72 @@ function OhhwellsBridge() {
25172
25270
  const [selectedMedia, setSelectedMedia] = (0, import_react21.useState)(null);
25173
25271
  const selectedMediaElRef = (0, import_react21.useRef)(null);
25174
25272
  const mediaKeyDropTokenRef = (0, import_react21.useRef)(0);
25273
+ const selectedIconElRef = (0, import_react21.useRef)(null);
25274
+ const selectedSocialElRef = (0, import_react21.useRef)(null);
25275
+ const queueSectionKeyDrop = (0, import_react21.useCallback)((prev) => {
25276
+ const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
25277
+ if (!sectionEl) return;
25278
+ const sectionInstanceId = instanceIdOf(movableUnit(sectionEl)) || null;
25279
+ if (!sectionInstanceId) return;
25280
+ const token = ++mediaKeyDropTokenRef.current;
25281
+ const onQueueDrained = (e) => {
25282
+ if (e.source !== window || e.data?.type !== "ow:media-key-drop" || e.data.token !== token) return;
25283
+ window.removeEventListener("message", onQueueDrained);
25284
+ if (aiSectionApiRef.current?.getSelectedId() !== sectionInstanceId) return;
25285
+ if (selectedMediaElRef.current || selectedIconElRef.current || selectedSocialElRef.current) return;
25286
+ postToParentRef.current({
25287
+ type: "ow:section-selected",
25288
+ sectionId: sectionEl.dataset.ohwSection ?? null,
25289
+ sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
25290
+ key: null
25291
+ });
25292
+ };
25293
+ window.addEventListener("message", onQueueDrained);
25294
+ window.postMessage({ type: "ow:media-key-drop", token }, "*");
25295
+ }, []);
25175
25296
  const clearMediaSelection = (0, import_react21.useCallback)(() => {
25176
25297
  const prev = selectedMediaElRef.current;
25177
25298
  selectedMediaElRef.current = null;
25178
25299
  setSelectedMedia(null);
25179
- const sectionEl = prev?.closest("[data-ohw-section]") ?? null;
25180
- if (sectionEl) {
25181
- const sectionInstanceId = instanceIdOf(movableUnit(sectionEl)) || null;
25182
- if (!sectionInstanceId) return;
25183
- const token = ++mediaKeyDropTokenRef.current;
25184
- const onQueueDrained = (e) => {
25185
- if (e.source !== window || e.data?.type !== "ow:media-key-drop" || e.data.token !== token) return;
25186
- window.removeEventListener("message", onQueueDrained);
25187
- if (aiSectionApiRef.current?.getSelectedId() !== sectionInstanceId) return;
25188
- if (selectedMediaElRef.current) return;
25189
- postToParentRef.current({
25190
- type: "ow:section-selected",
25191
- sectionId: sectionEl.dataset.ohwSection ?? null,
25192
- sectionLabel: sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? ""),
25193
- key: null
25194
- });
25195
- };
25196
- window.addEventListener("message", onQueueDrained);
25197
- window.postMessage({ type: "ow:media-key-drop", token }, "*");
25198
- }
25199
- }, []);
25300
+ queueSectionKeyDrop(prev);
25301
+ }, [queueSectionKeyDrop]);
25200
25302
  const clearMediaSelectionRef = (0, import_react21.useRef)(clearMediaSelection);
25201
25303
  clearMediaSelectionRef.current = clearMediaSelection;
25304
+ const clearIconSelection = (0, import_react21.useCallback)(() => {
25305
+ const prev = selectedIconElRef.current;
25306
+ if (!prev) return;
25307
+ selectedIconElRef.current = null;
25308
+ queueSectionKeyDrop(prev);
25309
+ }, [queueSectionKeyDrop]);
25310
+ const clearIconSelectionRef = (0, import_react21.useRef)(clearIconSelection);
25311
+ clearIconSelectionRef.current = clearIconSelection;
25312
+ const clearSocialSelection = (0, import_react21.useCallback)(() => {
25313
+ const prev = selectedSocialElRef.current;
25314
+ if (!prev) return;
25315
+ selectedSocialElRef.current = null;
25316
+ queueSectionKeyDrop(prev);
25317
+ }, [queueSectionKeyDrop]);
25318
+ const clearSocialSelectionRef = (0, import_react21.useRef)(clearSocialSelection);
25319
+ clearSocialSelectionRef.current = clearSocialSelection;
25320
+ const selectIconElement = (0, import_react21.useCallback)((el) => {
25321
+ if (selectedMediaElRef.current) clearMediaSelectionRef.current();
25322
+ selectedIconElRef.current = el;
25323
+ const sectionEl = el.closest("[data-ohw-section]");
25324
+ aiSectionApiRef.current?.selectFromElement(el, { report: false });
25325
+ postToParentRef.current({
25326
+ type: "ow:section-selected",
25327
+ sectionId: sectionEl?.dataset.ohwSection ?? null,
25328
+ sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
25329
+ key: el.dataset.ohwKey ?? null,
25330
+ keyLabel: "Icon"
25331
+ });
25332
+ }, []);
25333
+ const selectIconElementRef = (0, import_react21.useRef)(selectIconElement);
25334
+ selectIconElementRef.current = selectIconElement;
25202
25335
  const selectMediaElement = (0, import_react21.useCallback)((el) => {
25203
25336
  const r2 = el.getBoundingClientRect();
25204
25337
  const video = el.dataset.ohwEditable === "video" ? el.querySelector("video") : null;
25338
+ selectedIconElRef.current = null;
25205
25339
  selectedMediaElRef.current = el;
25206
25340
  setSelectedMedia({
25207
25341
  key: el.dataset.ohwKey ?? "",
@@ -25866,6 +26000,7 @@ function OhhwellsBridge() {
25866
26000
  selectedElRef.current = null;
25867
26001
  selectedHrefKeyRef.current = null;
25868
26002
  selectedFooterColAttrRef.current = null;
26003
+ clearSocialSelectionRef.current();
25869
26004
  setSelectedIsCta(false);
25870
26005
  setSelectedIsSocial(false);
25871
26006
  setSelectedIsSocialsRow(false);
@@ -26767,7 +26902,22 @@ function OhhwellsBridge() {
26767
26902
  const select = (0, import_react21.useCallback)((anchor) => {
26768
26903
  if (!isNavigationItem2(anchor)) return;
26769
26904
  if (activeElRef.current) deactivate();
26770
- aiSectionApiRef.current?.selectFromElement(anchor);
26905
+ clearSocialSelectionRef.current();
26906
+ const socialItem = getSocialItem(anchor);
26907
+ if (socialItem) {
26908
+ selectedSocialElRef.current = socialItem;
26909
+ const sectionEl = socialItem.closest("[data-ohw-section]");
26910
+ aiSectionApiRef.current?.selectFromElement(anchor, { report: false });
26911
+ postToParentRef.current({
26912
+ type: "ow:section-selected",
26913
+ sectionId: sectionEl?.dataset.ohwSection ?? null,
26914
+ sectionLabel: sectionEl ? sectionEl.dataset.ohwSectionLabel ?? titleCaseSectionId(sectionEl.dataset.ohwSection ?? "") : null,
26915
+ key: socialIconKey(socialItem) ?? socialItem.getAttribute("data-ohw-href-key"),
26916
+ keyLabel: socialPlatformLabel(socialItem, editContentRef.current)
26917
+ });
26918
+ } else {
26919
+ aiSectionApiRef.current?.selectFromElement(anchor);
26920
+ }
26771
26921
  selectedElRef.current = anchor;
26772
26922
  selectedHrefKeyRef.current = anchor.getAttribute("data-ohw-href-key");
26773
26923
  selectedFooterColAttrRef.current = null;
@@ -28076,6 +28226,9 @@ function OhhwellsBridge() {
28076
28226
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
28077
28227
  clearMediaSelectionRef.current();
28078
28228
  }
28229
+ if (selectedIconElRef.current && !selectedIconElRef.current.contains(target)) {
28230
+ clearIconSelectionRef.current();
28231
+ }
28079
28232
  if (selectedMediaElRef.current && target.closest("[data-ohw-media-overlay]") && !findClosestButtonLike(target)) {
28080
28233
  const hit = findImageAtPoint(e.clientX, e.clientY, false);
28081
28234
  if (hit && hit !== selectedMediaElRef.current) {
@@ -28246,9 +28399,13 @@ function OhhwellsBridge() {
28246
28399
  if (isIconEditable(editable) && !getSocialItem(editable)) {
28247
28400
  e.preventDefault();
28248
28401
  e.stopPropagation();
28249
- aiSectionApiRef.current?.selectFromElement(editable);
28250
28402
  const aiIcon = editable.hasAttribute("data-ai-icon");
28251
28403
  const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
28404
+ if (aiIcon) {
28405
+ aiSectionApiRef.current?.selectFromElement(editable);
28406
+ } else {
28407
+ selectIconElementRef.current(editable);
28408
+ }
28252
28409
  postToParentRef.current({
28253
28410
  type: "ow:icon-pick",
28254
28411
  key: editable.dataset.ohwKey ?? "",
@@ -29886,6 +30043,7 @@ function OhhwellsBridge() {
29886
30043
  deactivateRef.current();
29887
30044
  deselectRef.current();
29888
30045
  clearMediaSelectionRef.current();
30046
+ clearIconSelectionRef.current();
29889
30047
  };
29890
30048
  clearInteractionChromeRef.current = clearInteractionChrome;
29891
30049
  const applyTreeToLocalState = (payload) => {
@@ -30309,6 +30467,7 @@ function OhhwellsBridge() {
30309
30467
  deselectRef.current();
30310
30468
  deactivateRef.current();
30311
30469
  clearMediaSelectionRef.current();
30470
+ clearIconSelectionRef.current();
30312
30471
  };
30313
30472
  window.addEventListener("message", handleDeactivate);
30314
30473
  const handleToastAction = (e) => {
@@ -30398,6 +30557,10 @@ function OhhwellsBridge() {
30398
30557
  clearMediaSelectionRef.current();
30399
30558
  return;
30400
30559
  }
30560
+ if (e.key === "Escape" && selectedIconElRef.current) {
30561
+ clearIconSelectionRef.current();
30562
+ return;
30563
+ }
30401
30564
  if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a" && activeElRef.current) {
30402
30565
  e.preventDefault();
30403
30566
  selectAllTextInEditable(activeElRef.current);