@ohhwells/bridge 0.1.54-next.154 → 0.1.54-next.157

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
@@ -192,6 +192,59 @@ function deleteSectionFromState(state, sectionId) {
192
192
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
193
193
  }
194
194
 
195
+ // src/lib/brand-chrome.ts
196
+ var BRAND_NAME_KEY = "__ohw_brand_name";
197
+ var BRAND_TITLE_KEY = "__ohw_site_title";
198
+ var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
199
+ var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
200
+ BRAND_NAME_KEY,
201
+ BRAND_TITLE_KEY,
202
+ BRAND_FAVICON_LETTER_KEY
203
+ ]);
204
+ function upsertMeta(selector, attr, token, value) {
205
+ let el = document.head.querySelector(selector);
206
+ if (!el) {
207
+ el = document.createElement("meta");
208
+ el.setAttribute(attr, token);
209
+ document.head.appendChild(el);
210
+ }
211
+ if (el.getAttribute("content") !== value) el.setAttribute("content", value);
212
+ }
213
+ function escapeXml(value) {
214
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
215
+ }
216
+ function applyLetterFavicon(letter) {
217
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#111827"/><text x="32" y="46" font-family="system-ui,-apple-system,Segoe UI,sans-serif" font-size="40" font-weight="700" text-anchor="middle" fill="#ffffff">${escapeXml(letter)}</text></svg>`;
218
+ const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
219
+ let link = document.head.querySelector('link[rel="icon"]');
220
+ if (!link) {
221
+ link = document.createElement("link");
222
+ link.rel = "icon";
223
+ document.head.appendChild(link);
224
+ }
225
+ link.type = "image/svg+xml";
226
+ if (link.href !== href) link.href = href;
227
+ }
228
+ function applyBrandChrome(content) {
229
+ const name = content[BRAND_NAME_KEY];
230
+ if (typeof name === "string" && name.length > 0) {
231
+ document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
232
+ if (el.textContent !== name) el.textContent = name;
233
+ if (el.getAttribute("title") !== name) el.setAttribute("title", name);
234
+ });
235
+ }
236
+ const title = content[BRAND_TITLE_KEY];
237
+ if (typeof title === "string" && title.length > 0) {
238
+ if (document.title !== title) document.title = title;
239
+ upsertMeta('meta[property="og:title"]', "property", "og:title", title);
240
+ upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
241
+ }
242
+ const letter = content[BRAND_FAVICON_LETTER_KEY];
243
+ if (typeof letter === "string" && letter.length > 0) {
244
+ applyLetterFavicon(letter);
245
+ }
246
+ }
247
+
195
248
  // src/ui/ai-tree/aiSectionsManager.tsx
196
249
  var import_react_dom = require("react-dom");
197
250
  var import_client = require("react-dom/client");
@@ -9874,16 +9927,20 @@ function reconcileSocialsFromContent(content, root = document) {
9874
9927
  applySocialsOrder(stored, root);
9875
9928
  }
9876
9929
  var DROP_BAR_THICKNESS = 3;
9930
+ var DROP_BAR_GAP = 8;
9877
9931
  function buildSocialDropSlots(row) {
9878
9932
  const items = listSocialItems(row);
9879
9933
  if (!items.length) return [];
9880
- return items.concat(items[items.length - 1]).map((item, index) => {
9881
- const rect = item.getBoundingClientRect();
9882
- const left = (index === items.length ? rect.right : rect.left) - DROP_BAR_THICKNESS / 2;
9934
+ const rects = items.map((item) => item.getBoundingClientRect());
9935
+ return items.concat(items[items.length - 1]).map((_, index) => {
9936
+ const previous = rects[index - 1];
9937
+ const next = rects[index];
9938
+ const centre = previous && next ? (previous.right + next.left) / 2 : next ? next.left - DROP_BAR_GAP : previous.right + DROP_BAR_GAP;
9939
+ const rect = next ?? previous;
9883
9940
  return {
9884
9941
  insertIndex: index,
9885
9942
  columnIndex: -1,
9886
- left,
9943
+ left: centre - DROP_BAR_THICKNESS / 2,
9887
9944
  top: rect.top,
9888
9945
  width: DROP_BAR_THICKNESS,
9889
9946
  height: rect.height,
@@ -15291,11 +15348,13 @@ function OhhwellsBridge() {
15291
15348
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
15292
15349
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
15293
15350
  }
15351
+ applyBrandChrome(content);
15294
15352
  for (const [key, val] of Object.entries(content)) {
15295
15353
  if (key === "__ohw_sections") continue;
15296
15354
  if (key === AI_SECTIONS_KEY) continue;
15297
15355
  if (key === LOGO_PLACEHOLDER_KEY) continue;
15298
15356
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
15357
+ if (BRAND_CHROME_KEYS.has(key)) continue;
15299
15358
  if (applyVideoSettingNode(key, val)) continue;
15300
15359
  if (applyCarouselNode(key, val)) continue;
15301
15360
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -15376,10 +15435,12 @@ function OhhwellsBridge() {
15376
15435
  retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
15377
15436
  observer?.disconnect();
15378
15437
  try {
15438
+ applyBrandChrome(content);
15379
15439
  for (const [key, val] of Object.entries(content)) {
15380
15440
  if (key === "__ohw_sections") continue;
15381
15441
  if (key === LOGO_PLACEHOLDER_KEY) continue;
15382
15442
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
15443
+ if (BRAND_CHROME_KEYS.has(key)) continue;
15383
15444
  if (applyVideoSettingNode(key, val)) continue;
15384
15445
  if (applyCarouselNode(key, val)) continue;
15385
15446
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -15405,7 +15466,6 @@ function OhhwellsBridge() {
15405
15466
  reconcileFooterOrderFromContent(content);
15406
15467
  reconcileSocialsFromContent(content);
15407
15468
  applySocialsDisplayFromContent(content);
15408
- applySocialsDisplayFromContent(content);
15409
15469
  } finally {
15410
15470
  observer?.observe(document.body, { childList: true, subtree: true });
15411
15471
  }
@@ -15485,7 +15545,6 @@ function OhhwellsBridge() {
15485
15545
  reconcileFooterOrderFromContent(content);
15486
15546
  reconcileSocialsFromContent(content);
15487
15547
  applySocialsDisplayFromContent(content);
15488
- applySocialsDisplayFromContent(content);
15489
15548
  document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
15490
15549
  if (isFooterHrefKey(el.getAttribute("data-ohw-href-key"))) {
15491
15550
  disableNativeHrefDrag(el);
@@ -16027,7 +16086,7 @@ function OhhwellsBridge() {
16027
16086
  const selected = selectedElRef.current;
16028
16087
  if (selected && (selected === editable || selected.contains(editable))) return;
16029
16088
  if (!isMediaEditable(editable) && !editable.hasAttribute("contenteditable")) {
16030
- if (isIconEditable(editable)) {
16089
+ if (isIconEditable(editable) && !getSocialItem(editable)) {
16031
16090
  hoveredItemElRef.current = editable;
16032
16091
  setHoveredItemRect(editable.getBoundingClientRect());
16033
16092
  return;
@@ -16097,7 +16156,7 @@ function OhhwellsBridge() {
16097
16156
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
16098
16157
  if (related?.closest("[data-ohw-drag-handle-container], [data-ohw-item-interaction]")) return;
16099
16158
  if (!isMediaEditable(editable)) {
16100
- if (isIconEditable(editable) && hoveredItemElRef.current === editable) {
16159
+ if (isIconEditable(editable) && !getSocialItem(editable) && hoveredItemElRef.current === editable) {
16101
16160
  if (!related?.closest("[data-ohw-item-interaction]")) {
16102
16161
  hoveredItemElRef.current = null;
16103
16162
  setHoveredItemRect(null);
@@ -16937,6 +16996,7 @@ function OhhwellsBridge() {
16937
16996
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
16938
16997
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16939
16998
  }
16999
+ applyBrandChrome(content);
16940
17000
  let sectionsJson = null;
16941
17001
  for (const [key, val] of Object.entries(content)) {
16942
17002
  if (key === "__ohw_sections") {
@@ -16946,6 +17006,7 @@ function OhhwellsBridge() {
16946
17006
  if (key === AI_SECTIONS_KEY) continue;
16947
17007
  if (key === LOGO_PLACEHOLDER_KEY) continue;
16948
17008
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
17009
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16949
17010
  if (applyVideoSettingNode(key, val)) continue;
16950
17011
  if (applyCarouselNode(key, val)) continue;
16951
17012
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17768,9 +17829,9 @@ function OhhwellsBridge() {
17768
17829
  return;
17769
17830
  }
17770
17831
  if (target.closest("[data-ohw-item-drag-surface]")) return;
17771
- const anchor = getNavigationItemAnchor(target);
17832
+ const anchor = getNavigationItemAnchor(target) ?? (document.elementsFromPoint(e.clientX, e.clientY).map((el) => el instanceof HTMLElement ? getNavigationItemAnchor(el) : null).find((found) => found !== null) ?? null);
17772
17833
  const hrefKey = anchor?.getAttribute("data-ohw-href-key") ?? null;
17773
- if (anchor && isFooterHrefKey(hrefKey)) {
17834
+ if (anchor && (isFooterHrefKey(hrefKey) || getSocialItem(anchor))) {
17774
17835
  footerPointerDragRef.current = {
17775
17836
  el: anchor,
17776
17837
  kind: "link",
@@ -17830,7 +17891,7 @@ function OhhwellsBridge() {
17830
17891
  const column = findFooterColumnForLink(pending.el);
17831
17892
  const columns2 = listFooterColumns();
17832
17893
  beginFooterDragRef.current({
17833
- kind: "link",
17894
+ kind: getSocialItem(pending.el) ? "social" : "link",
17834
17895
  hrefKey: key,
17835
17896
  columnEl: column,
17836
17897
  sourceColumnIndex: column ? columns2.indexOf(column) : 0,