@ohhwells/bridge 0.1.64-next.177 → 0.1.65-next.181

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
@@ -157,7 +157,12 @@ function parseAiSectionsState(raw) {
157
157
  media: entry.media && typeof entry.media === "object" ? entry.media : {}
158
158
  }));
159
159
  const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
160
- return { v: 1, sections, ...removed.length ? { removed } : {} };
160
+ return {
161
+ v: 1,
162
+ sections,
163
+ ...removed.length ? { removed } : {},
164
+ ...parsed.hideTemplate === true ? { hideTemplate: true } : {}
165
+ };
161
166
  } catch {
162
167
  return EMPTY_AI_SECTIONS;
163
168
  }
@@ -534,6 +539,36 @@ var FEATURE_LINE_CSS = [
534
539
  `background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
535
540
  `mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
536
541
  ].join("");
542
+ function hexLuminance(color) {
543
+ const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
544
+ if (!m) return null;
545
+ const [r2, g, b] = [0, 2, 4].map((i) => {
546
+ const c = parseInt(m[1].slice(i, i + 2), 16) / 255;
547
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
548
+ });
549
+ return 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
550
+ }
551
+ function hexContrast(a, b) {
552
+ const la = hexLuminance(a);
553
+ const lb = hexLuminance(b);
554
+ if (la === null || lb === null) return null;
555
+ const [hi, lo] = la > lb ? [la, lb] : [lb, la];
556
+ return (hi + 0.05) / (lo + 0.05);
557
+ }
558
+ function accentBandContext(brand) {
559
+ const p = brand.palette;
560
+ const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
561
+ if (lightWins) {
562
+ return {
563
+ brand: { ...brand, palette: { dark: p.light, primary: p.light, accent: p.light, light: p.primary } },
564
+ buttonLabel: p.primary
565
+ };
566
+ }
567
+ return {
568
+ brand: { ...brand, palette: { dark: p.dark, primary: p.dark, accent: p.dark, light: p.light } },
569
+ buttonLabel: p.light
570
+ };
571
+ }
537
572
  function textAttrs(ctx, path) {
538
573
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
539
574
  }
@@ -641,7 +676,7 @@ function ButtonEl({
641
676
  textDecoration: "none",
642
677
  cursor: "pointer",
643
678
  ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body),
644
- ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: AI_TREE_TOKENS.textPrimaryForeground }
679
+ ...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? AI_TREE_TOKENS.textPrimaryForeground }
645
680
  },
646
681
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
647
682
  }
@@ -1543,11 +1578,14 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1543
1578
  return null;
1544
1579
  }
1545
1580
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1581
+ const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1582
+ const blockBrand = band?.brand ?? resolvedBrand;
1546
1583
  const ctx = {
1547
- brand: resolvedBrand,
1584
+ brand: blockBrand,
1548
1585
  resolveMedia: resolveMedia ?? (() => null),
1549
- cardSurface: resolvedBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${resolvedBrand.palette.light} 90%, ${resolvedBrand.palette.dark})`,
1550
- keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null
1586
+ cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1587
+ keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1588
+ ...band ? { buttonLabel: band.buttonLabel } : {}
1551
1589
  };
1552
1590
  const settings = tree.settings ?? {};
1553
1591
  const pad = AI_TREE_TOKENS.sectionPadding[settings.spacing ?? "balanced"];
@@ -1582,7 +1620,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1582
1620
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1583
1621
  backgroundSize: "cover",
1584
1622
  backgroundPosition: "center",
1585
- color: settings.sectionBackground === "accent" ? resolvedBrand.palette.light : void 0
1623
+ color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1586
1624
  },
1587
1625
  children: [
1588
1626
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
@@ -1639,6 +1677,8 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
1639
1677
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1640
1678
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1641
1679
  var REMOVED_ATTR = "data-ohw-ai-removed";
1680
+ var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
1681
+ var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
1642
1682
  function readRootVar(name) {
1643
1683
  if (typeof document === "undefined") return "";
1644
1684
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
@@ -1738,6 +1778,24 @@ function syncRemovedSections(state) {
1738
1778
  }
1739
1779
  }
1740
1780
  }
1781
+ function syncTemplateHidden(state, pageHasSections) {
1782
+ const hide = state.hideTemplate === true && pageHasSections;
1783
+ for (const el of Array.from(document.querySelectorAll(`[${TEMPLATE_HIDDEN_ATTR}]`))) {
1784
+ if (!hide) {
1785
+ el.style.removeProperty("display");
1786
+ el.removeAttribute(TEMPLATE_HIDDEN_ATTR);
1787
+ }
1788
+ }
1789
+ if (!hide) return;
1790
+ for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
1791
+ if (el.hasAttribute(CONTAINER_ATTR)) continue;
1792
+ if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
1793
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
1794
+ if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
1795
+ el.style.display = "none";
1796
+ el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
1797
+ }
1798
+ }
1741
1799
  function syncReplacedOriginals(state) {
1742
1800
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1743
1801
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1808,6 +1866,7 @@ function applyAiSectionsToDom(state, options) {
1808
1866
  }
1809
1867
  syncReplacedOriginals(state);
1810
1868
  syncRemovedSections(state);
1869
+ syncTemplateHidden(state, pageSections.length > 0);
1811
1870
  }
1812
1871
 
1813
1872
  // src/useLinkHrefGuardian.ts
@@ -7652,6 +7711,9 @@ var import_lucide_react7 = require("lucide-react");
7652
7711
 
7653
7712
  // src/lib/sections.ts
7654
7713
  var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
7714
+ function isChromeSection(el) {
7715
+ return el.matches("header, nav, footer, aside");
7716
+ }
7655
7717
  function titleCaseSectionId(id) {
7656
7718
  return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
7657
7719
  }
@@ -7662,6 +7724,8 @@ function parseSectionsFromRoot(root) {
7662
7724
  const id = el.getAttribute("data-ohw-section") ?? "";
7663
7725
  if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
7664
7726
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
7727
+ if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
7728
+ continue;
7665
7729
  seen.add(id);
7666
7730
  const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
7667
7731
  sections.push({ id, label });
@@ -7722,7 +7786,7 @@ function useLiveSectionRect(sectionId) {
7722
7786
  }
7723
7787
  function computeSectionBoundaryFlags(instanceId) {
7724
7788
  const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7725
- (el) => !el.parentElement?.closest("[data-ohw-section]")
7789
+ (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
7726
7790
  );
7727
7791
  const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7728
7792
  if (index === -1) return { isFirst: true, isLast: true };
@@ -7919,7 +7983,8 @@ function AiSectionOverlay({
7919
7983
  const reviewRect = useLiveSectionRect(reviewId);
7920
7984
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
7921
7985
  (0, import_react8.useEffect)(() => {
7922
- if (!activeSelectionId || !selectionRect) {
7986
+ const selectedEl = activeSelectionId ? findSectionElement(activeSelectionId) : null;
7987
+ if (!activeSelectionId || !selectionRect || selectedEl && isChromeSection(selectedEl)) {
7923
7988
  postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
7924
7989
  return;
7925
7990
  }
@@ -16558,14 +16623,14 @@ function OhhwellsBridge() {
16558
16623
  }
16559
16624
  const applyContent = (content) => {
16560
16625
  const imageLoads = [];
16561
- if (typeof content[AI_SECTIONS_KEY] === "string") {
16562
- aiSectionsRef.current = content[AI_SECTIONS_KEY];
16563
- applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16564
- }
16565
16626
  if (typeof content[BRAND_KIT_KEY] === "string") {
16566
16627
  brandKitRef.current = content[BRAND_KIT_KEY];
16567
16628
  applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
16568
16629
  }
16630
+ if (typeof content[AI_SECTIONS_KEY] === "string") {
16631
+ aiSectionsRef.current = content[AI_SECTIONS_KEY];
16632
+ applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16633
+ }
16569
16634
  if (typeof content[STYLE_STORE_KEY] === "string") {
16570
16635
  stylesRef.current = content[STYLE_STORE_KEY];
16571
16636
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -18436,14 +18501,14 @@ function OhhwellsBridge() {
18436
18501
  if (e.data?.type !== "ow:hydrate") return;
18437
18502
  const content = e.data.content;
18438
18503
  if (!content) return;
18439
- if (typeof content[AI_SECTIONS_KEY] === "string") {
18440
- aiSectionsRef.current = content[AI_SECTIONS_KEY];
18441
- applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
18442
- }
18443
18504
  if (typeof content[BRAND_KIT_KEY] === "string") {
18444
18505
  brandKitRef.current = content[BRAND_KIT_KEY];
18445
18506
  applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
18446
18507
  }
18508
+ if (typeof content[AI_SECTIONS_KEY] === "string") {
18509
+ aiSectionsRef.current = content[AI_SECTIONS_KEY];
18510
+ applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
18511
+ }
18447
18512
  if (typeof content[STYLE_STORE_KEY] === "string") {
18448
18513
  stylesRef.current = content[STYLE_STORE_KEY];
18449
18514
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -19515,7 +19580,7 @@ function OhhwellsBridge() {
19515
19580
  postToParent2({
19516
19581
  type: "ow:ready",
19517
19582
  version: "1",
19518
- bridgeVersion: "0.1.64",
19583
+ bridgeVersion: "0.1.65",
19519
19584
  path: pathname,
19520
19585
  nodes: collectEditableNodes(editContentRef.current),
19521
19586
  sections