@ohhwells/bridge 0.1.160-next.427 → 0.1.160-next.428

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
@@ -1208,21 +1208,21 @@ function chromeFrom(sample) {
1208
1208
  };
1209
1209
  }
1210
1210
  function filledPrimary(seed) {
1211
- const paint = parseColor(paintColor(seed)) ?? { r: 15, g: 15, b: 15, a: 1 };
1212
- const luminance = relativeLuminance(paint);
1211
+ const paint2 = parseColor(paintColor(seed)) ?? { r: 15, g: 15, b: 15, a: 1 };
1212
+ const luminance = relativeLuminance(paint2);
1213
1213
  return {
1214
- background: toCss({ ...paint, a: 1 }),
1214
+ background: toCss({ ...paint2, a: 1 }),
1215
1215
  color: luminance > 0.55 ? "rgb(15, 15, 15)" : "rgb(255, 255, 255)",
1216
1216
  borderColor: TRANSPARENT,
1217
1217
  borderWidth: "0px"
1218
1218
  };
1219
1219
  }
1220
1220
  function generatedSecondary(primary) {
1221
- const paint = accentColor(primary);
1221
+ const paint2 = accentColor(primary);
1222
1222
  return {
1223
1223
  background: TRANSPARENT,
1224
- color: paint,
1225
- borderColor: paint,
1224
+ color: paint2,
1225
+ borderColor: paint2,
1226
1226
  borderWidth: "1px"
1227
1227
  };
1228
1228
  }
@@ -1235,9 +1235,9 @@ function generatedGhost(primary) {
1235
1235
  };
1236
1236
  }
1237
1237
  function normalizeOutlineChrome(outline, primary) {
1238
- const paint = accentColor(primary);
1238
+ const paint2 = accentColor(primary);
1239
1239
  const text = parseColor(outline.color);
1240
- const paintRgb = parseColor(paint);
1240
+ const paintRgb = parseColor(paint2);
1241
1241
  const primaryLabel = parseColor(primary.color);
1242
1242
  const matchesOnPrimary = Boolean(text && primaryLabel) && colorKey(outline.color) === colorKey(primary.color);
1243
1243
  const textTooLight = text ? relativeLuminance(text) > 0.7 : false;
@@ -1246,8 +1246,8 @@ function normalizeOutlineChrome(outline, primary) {
1246
1246
  const hasBorder = parseFloat(outline.borderWidth) > 0;
1247
1247
  return {
1248
1248
  ...outline,
1249
- color: paint,
1250
- borderColor: hasBorder ? paint : outline.borderColor
1249
+ color: paint2,
1250
+ borderColor: hasBorder ? paint2 : outline.borderColor
1251
1251
  };
1252
1252
  }
1253
1253
  function generateButtonVariants(samples) {
@@ -7430,6 +7430,120 @@ function findSelectableNavContainer(doc = document) {
7430
7430
  );
7431
7431
  }
7432
7432
 
7433
+ // src/lib/youtube.ts
7434
+ var YOUTUBE_ID_PATTERN = /^[A-Za-z0-9_-]{11}$/;
7435
+ function extractYouTubeId(input) {
7436
+ const trimmed = input.trim();
7437
+ if (!trimmed) return null;
7438
+ if (YOUTUBE_ID_PATTERN.test(trimmed)) return trimmed;
7439
+ let url;
7440
+ try {
7441
+ url = new URL(trimmed);
7442
+ } catch {
7443
+ return null;
7444
+ }
7445
+ const host = url.hostname.replace(/^www\./, "");
7446
+ if (host !== "youtube.com" && host !== "m.youtube.com" && host !== "youtu.be" && host !== "youtube-nocookie.com") {
7447
+ return null;
7448
+ }
7449
+ let candidate = null;
7450
+ if (host === "youtu.be") {
7451
+ candidate = url.pathname.slice(1).split("/")[0] ?? null;
7452
+ } else if (url.pathname === "/watch") {
7453
+ candidate = url.searchParams.get("v");
7454
+ } else {
7455
+ const match = url.pathname.match(/^\/(?:embed|shorts)\/([^/]+)/);
7456
+ candidate = match ? match[1] : null;
7457
+ }
7458
+ return candidate && YOUTUBE_ID_PATTERN.test(candidate) ? candidate : null;
7459
+ }
7460
+
7461
+ // src/lib/section-embed.ts
7462
+ var SECTION_EMBED_SUFFIX = "__ohw_embed";
7463
+ function readStoredEmbed(value) {
7464
+ if (value.startsWith("start\n")) {
7465
+ return { raw: value.slice("start\n".length), at: "start" };
7466
+ }
7467
+ return { raw: value, at: "end" };
7468
+ }
7469
+ function embedFrameSrc(value) {
7470
+ const videoId = extractYouTubeId(value);
7471
+ if (videoId) {
7472
+ return { src: `https://www.youtube-nocookie.com/embed/${videoId}?rel=0`, title: "YouTube video" };
7473
+ }
7474
+ let url;
7475
+ try {
7476
+ url = new URL(value);
7477
+ } catch {
7478
+ return null;
7479
+ }
7480
+ const host = url.hostname.replace(/^www\./, "").toLowerCase();
7481
+ if (host === "google.com" && url.pathname.startsWith("/maps") || host === "maps.google.com") {
7482
+ const query = url.searchParams.get("q") || url.searchParams.get("query") || "";
7483
+ const src = query ? `https://maps.google.com/maps?q=${encodeURIComponent(query)}&output=embed` : `https://maps.google.com/maps?output=embed`;
7484
+ return { src, title: "Map" };
7485
+ }
7486
+ if (host === "maps.app.goo.gl") {
7487
+ return { src: value, title: "Map" };
7488
+ }
7489
+ if (host === "calendly.com" || host.endsWith(".calendly.com") || host === "calendar.google.com") {
7490
+ return { src: value, title: "Calendar" };
7491
+ }
7492
+ if (host === "docs.google.com" && url.pathname.includes("/forms/")) {
7493
+ return { src: value.replace(/\/edit(?:\?.*)?$/i, "/viewform"), title: "Form" };
7494
+ }
7495
+ if (host === "forms.gle") {
7496
+ return { src: value, title: "Form" };
7497
+ }
7498
+ return null;
7499
+ }
7500
+ function place(section, box, at) {
7501
+ if (at === "start") {
7502
+ section.insertBefore(box, section.firstChild);
7503
+ return;
7504
+ }
7505
+ section.appendChild(box);
7506
+ }
7507
+ function paint(section, key, src, title, at) {
7508
+ const existing = section.querySelector(`[data-ohw-section-embed="${CSS.escape(key)}"]`);
7509
+ const frame = existing?.querySelector("iframe");
7510
+ if (existing && frame) {
7511
+ if (frame.getAttribute("src") !== src) frame.setAttribute("src", src);
7512
+ frame.title = title;
7513
+ place(section, existing, at);
7514
+ return;
7515
+ }
7516
+ existing?.remove();
7517
+ const box = document.createElement("div");
7518
+ box.setAttribute("data-ohw-section-embed", key);
7519
+ box.style.cssText = at === "start" ? "width:100%;max-width:960px;margin:0 auto 32px;padding:32px 24px 0;box-sizing:border-box;" : "width:100%;max-width:960px;margin:32px auto 0;padding:0 24px 32px;box-sizing:border-box;";
7520
+ const frameWrap = document.createElement("div");
7521
+ frameWrap.style.cssText = "position:relative;width:100%;aspect-ratio:16/9;min-height:250px;border-radius:12px;overflow:hidden;background:#0f172a;";
7522
+ const iframe = document.createElement("iframe");
7523
+ iframe.src = src;
7524
+ iframe.title = title;
7525
+ iframe.style.cssText = "position:absolute;inset:0;width:100%;height:100%;border:0;";
7526
+ iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture";
7527
+ iframe.setAttribute("allowfullscreen", "");
7528
+ frameWrap.appendChild(iframe);
7529
+ box.appendChild(frameWrap);
7530
+ place(section, box, at);
7531
+ }
7532
+ function applySectionEmbeds(content) {
7533
+ for (const [key, val] of Object.entries(content)) {
7534
+ if (!key.endsWith(SECTION_EMBED_SUFFIX)) continue;
7535
+ const sectionId = key.slice(0, -SECTION_EMBED_SUFFIX.length);
7536
+ const section = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7537
+ const stored = typeof val === "string" ? readStoredEmbed(val) : null;
7538
+ const frame = stored ? embedFrameSrc(stored.raw) : null;
7539
+ if (!section || !stored || !frame) {
7540
+ section?.querySelector(`[data-ohw-section-embed="${CSS.escape(key)}"]`)?.remove();
7541
+ continue;
7542
+ }
7543
+ paint(section, key, frame.src, frame.title, stored.at);
7544
+ }
7545
+ }
7546
+
7433
7547
  // src/ui/ai-tree/aiSectionsManager.tsx
7434
7548
  var import_react_dom = require("react-dom");
7435
7549
  var import_client = require("react-dom/client");
@@ -11563,6 +11677,34 @@ function minSpanForFooterBlock(type) {
11563
11677
  return 3;
11564
11678
  }
11565
11679
  }
11680
+ function readLiveSocialItems() {
11681
+ const root = getFooterRoot();
11682
+ const row = root?.querySelector("[data-ohw-socials-row]");
11683
+ if (!row) return [];
11684
+ const items = [];
11685
+ for (const anchor of Array.from(row.querySelectorAll("a[href]"))) {
11686
+ const href = anchor.getAttribute("href")?.trim() ?? "";
11687
+ const aria = anchor.getAttribute("aria-label")?.trim().toLowerCase() ?? "";
11688
+ const platform = FOOTER_SOCIAL_PLATFORMS.find((name) => aria === name) ?? FOOTER_SOCIAL_PLATFORMS.find((name) => href.toLowerCase().includes(name));
11689
+ if (!platform || !/^https?:\/\//i.test(href)) continue;
11690
+ items.push({ platform, href });
11691
+ }
11692
+ return items;
11693
+ }
11694
+ function syncSocialRowsFromDom(tree) {
11695
+ const root = getFooterRoot();
11696
+ if (!root) return;
11697
+ const row = root.querySelector("[data-ohw-socials-row]");
11698
+ const live = readLiveSocialItems();
11699
+ if (row && live.length === 0) return;
11700
+ for (const col of tree.columns) {
11701
+ col.blocks = col.blocks.flatMap((block) => {
11702
+ if (block.type !== "social-row") return [block];
11703
+ if (!row) return [];
11704
+ return [{ ...block, items: live.map((item) => ({ platform: item.platform, href: item.href })) }];
11705
+ });
11706
+ }
11707
+ }
11566
11708
  function collectBrandBlocks(tree) {
11567
11709
  const brand = [];
11568
11710
  for (const col of tree.columns) {
@@ -11808,6 +11950,7 @@ function textOf2(el) {
11808
11950
  }
11809
11951
  function syncFooterTreeLinksFromDom(tree) {
11810
11952
  const next = cloneTree(tree);
11953
+ syncSocialRowsFromDom(next);
11811
11954
  const domCols = listFooterColumns().map((col) => {
11812
11955
  const heading = getFooterColumnHeadingEl(col);
11813
11956
  const title = heading ? textOf2(heading) : void 0;
@@ -17384,7 +17527,7 @@ function apiBase() {
17384
17527
  function bridgeVersion() {
17385
17528
  try {
17386
17529
  if (true) {
17387
- return "0.1.160-next.427";
17530
+ return "0.1.160-next.428";
17388
17531
  }
17389
17532
  } catch {
17390
17533
  }
@@ -17398,7 +17541,7 @@ function stampBridgeVersionOnDocument() {
17398
17541
  return;
17399
17542
  }
17400
17543
  try {
17401
- const v = true ? "0.1.160-next.427" : "";
17544
+ const v = true ? "0.1.160-next.428" : "";
17402
17545
  if (v) {
17403
17546
  document.documentElement.setAttribute("data-ohw-bridge-version", v);
17404
17547
  window.__OHW_BRIDGE_VERSION__ = v;
@@ -20246,34 +20389,6 @@ function clearVideo(tree, path) {
20246
20389
  });
20247
20390
  }
20248
20391
 
20249
- // src/lib/youtube.ts
20250
- var YOUTUBE_ID_PATTERN = /^[A-Za-z0-9_-]{11}$/;
20251
- function extractYouTubeId(input) {
20252
- const trimmed = input.trim();
20253
- if (!trimmed) return null;
20254
- if (YOUTUBE_ID_PATTERN.test(trimmed)) return trimmed;
20255
- let url;
20256
- try {
20257
- url = new URL(trimmed);
20258
- } catch {
20259
- return null;
20260
- }
20261
- const host = url.hostname.replace(/^www\./, "");
20262
- if (host !== "youtube.com" && host !== "m.youtube.com" && host !== "youtu.be" && host !== "youtube-nocookie.com") {
20263
- return null;
20264
- }
20265
- let candidate = null;
20266
- if (host === "youtu.be") {
20267
- candidate = url.pathname.slice(1).split("/")[0] ?? null;
20268
- } else if (url.pathname === "/watch") {
20269
- candidate = url.searchParams.get("v");
20270
- } else {
20271
- const match = url.pathname.match(/^\/(?:embed|shorts)\/([^/]+)/);
20272
- candidate = match ? match[1] : null;
20273
- }
20274
- return candidate && YOUTUBE_ID_PATTERN.test(candidate) ? candidate : null;
20275
- }
20276
-
20277
20392
  // src/ui/ai-section/AiVideoOverlay.tsx
20278
20393
  var import_jsx_runtime23 = require("react/jsx-runtime");
20279
20394
  var PRIMARY5 = "#0885FE";
@@ -28192,6 +28307,7 @@ function OhhwellsBridge() {
28192
28307
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28193
28308
  continue;
28194
28309
  }
28310
+ if (key.endsWith(SECTION_EMBED_SUFFIX)) continue;
28195
28311
  if (applyVideoSettingNode(key, val)) continue;
28196
28312
  if (applyCarouselNode(key, val)) continue;
28197
28313
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -28231,6 +28347,7 @@ function OhhwellsBridge() {
28231
28347
  });
28232
28348
  applyLinkByKey(key, val);
28233
28349
  }
28350
+ applySectionEmbeds(content);
28234
28351
  applyLogoFromContent(content);
28235
28352
  applyLogoSizes(content);
28236
28353
  reconcileNavbarItemsFromContent(content);
@@ -28434,6 +28551,7 @@ function OhhwellsBridge() {
28434
28551
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28435
28552
  continue;
28436
28553
  }
28554
+ if (key.endsWith(SECTION_EMBED_SUFFIX)) continue;
28437
28555
  if (applyVideoSettingNode(key, val)) continue;
28438
28556
  if (applyCarouselNode(key, val)) continue;
28439
28557
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -28458,6 +28576,7 @@ function OhhwellsBridge() {
28458
28576
  });
28459
28577
  applyLinkByKey(key, val);
28460
28578
  }
28579
+ applySectionEmbeds(content);
28461
28580
  applyLogoFromContent(content);
28462
28581
  reconcileNavbarItemsFromContent(content);
28463
28582
  reconcileFooterOrderFromContent(content);
@@ -30716,6 +30835,7 @@ function OhhwellsBridge() {
30716
30835
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
30717
30836
  continue;
30718
30837
  }
30838
+ if (key.endsWith(SECTION_EMBED_SUFFIX)) continue;
30719
30839
  if (applyVideoSettingNode(key, val)) continue;
30720
30840
  if (applyCarouselNode(key, val)) continue;
30721
30841
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -30741,6 +30861,7 @@ function OhhwellsBridge() {
30741
30861
  });
30742
30862
  applyLinkByKey(key, val);
30743
30863
  }
30864
+ applySectionEmbeds(content);
30744
30865
  applyLogoFromContent(content);
30745
30866
  applyLogoSizes(content);
30746
30867
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
@@ -32486,7 +32607,7 @@ function OhhwellsBridge() {
32486
32607
  postToParent2({
32487
32608
  type: "ow:ready",
32488
32609
  version: "1",
32489
- bridgeVersion: "0.1.160-next.427",
32610
+ bridgeVersion: "0.1.160-next.428",
32490
32611
  path: pathname,
32491
32612
  nodes: collectEditableNodes(editContentRef.current),
32492
32613
  sections