@ohhwells/bridge 0.1.139-next.393 → 0.1.139-next.398

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
@@ -87,6 +87,7 @@ __export(index_exports, {
87
87
  resolveFooterCopyright: () => resolveFooterCopyright,
88
88
  serializeAiFooterState: () => serializeAiFooterState,
89
89
  stepViableSlide: () => stepViableSlide,
90
+ stripTextAlignCss: () => stripTextAlignCss,
90
91
  toggleVariants: () => toggleVariants,
91
92
  useOhwCarousel: () => useOhwCarousel,
92
93
  validateUrlInput: () => validateUrlInput,
@@ -1718,7 +1719,8 @@ function labelElement2(el) {
1718
1719
  return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? el.querySelector("[data-ohw-key]");
1719
1720
  }
1720
1721
  function buttonLabel(el) {
1721
- return (labelElement2(el)?.textContent ?? el.textContent ?? "").trim();
1722
+ const label = labelElement2(el);
1723
+ return (label ? label.innerHTML : el.textContent ?? "").trim();
1722
1724
  }
1723
1725
  function buttonHref(el) {
1724
1726
  return el.getAttribute("href") ?? "";
@@ -1857,7 +1859,7 @@ function duplicateButton(source, content) {
1857
1859
  stampVariantClass(copy, copyAssignment.variant);
1858
1860
  applyButtonStyle(copy, copyAssignment, paintedTokensOf(source) ?? tokens);
1859
1861
  const copyLabelEl = labelElement2(copy);
1860
- if (copyLabelEl) copyLabelEl.textContent = label;
1862
+ if (copyLabelEl) copyLabelEl.innerHTML = label;
1861
1863
  let collection = buttonCollectionOf(source);
1862
1864
  const collections = readCollections(content);
1863
1865
  let collectionId = collection?.getAttribute(COLLECTION_ATTR) ?? "";
@@ -2183,7 +2185,7 @@ function applySnapshotContent(el, snapshot, content) {
2183
2185
  const label = labelElement2(el);
2184
2186
  if (label) {
2185
2187
  if (label.getAttribute("data-ohw-key") !== labelKey) label.setAttribute("data-ohw-key", labelKey);
2186
- if (label.textContent !== text) label.textContent = text;
2188
+ if (label.innerHTML !== text) label.innerHTML = text;
2187
2189
  }
2188
2190
  }
2189
2191
  function reconcileButtonCollections(doc, content) {
@@ -4268,7 +4270,7 @@ function buildNavLink(index, href, label, template) {
4268
4270
  const span = document.createElement("span");
4269
4271
  span.setAttribute("data-ohw-editable", "text");
4270
4272
  span.setAttribute("data-ohw-key", `nav-${index}-label`);
4271
- span.textContent = label;
4273
+ span.innerHTML = label;
4272
4274
  anchor.appendChild(span);
4273
4275
  return anchor;
4274
4276
  }
@@ -6266,6 +6268,26 @@ function applyTreeToState(state, payload) {
6266
6268
  const others = state.sections.filter((existing) => existing.id !== entry.id);
6267
6269
  return { ...state, v: 1, sections: [...others, entry] };
6268
6270
  }
6271
+ function stripTextAlignCss(html) {
6272
+ return html.replace(/\sstyle="([^"]*)"/gi, (whole, value) => {
6273
+ if (!/text-align\s*:/i.test(value)) return whole;
6274
+ const kept = value.split(";").map((decl) => decl.trim()).filter((decl) => decl && !/^text-align\s*:/i.test(decl)).join("; ");
6275
+ return kept ? ` style="${kept}"` : "";
6276
+ });
6277
+ }
6278
+ function stripTextAlignInValue(value) {
6279
+ if (typeof value === "string") return stripTextAlignCss(value);
6280
+ if (Array.isArray(value)) return value.map(stripTextAlignInValue);
6281
+ if (value && typeof value === "object") {
6282
+ return Object.fromEntries(
6283
+ Object.entries(value).map(([key, inner]) => [
6284
+ key,
6285
+ stripTextAlignInValue(inner)
6286
+ ])
6287
+ );
6288
+ }
6289
+ return value;
6290
+ }
6269
6291
  function foldAlignIntoTrees(state, store) {
6270
6292
  const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
6271
6293
  const nextTrees = /* @__PURE__ */ new Map();
@@ -6288,6 +6310,12 @@ function foldAlignIntoTrees(state, store) {
6288
6310
  return stripChildAligns(rest);
6289
6311
  })
6290
6312
  } : node;
6313
+ const stripContentAligns = (node) => ({
6314
+ ...node,
6315
+ ...node.slots ? { slots: stripTextAlignInValue(node.slots) } : {},
6316
+ ...node.children ? { children: node.children.map(stripContentAligns) } : {}
6317
+ });
6318
+ const authoritative = (block) => stripChildAligns(stripContentAligns(block));
6291
6319
  const nodes = {};
6292
6320
  for (const [key, override] of Object.entries(store.nodes)) {
6293
6321
  const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
@@ -6299,7 +6327,7 @@ function foldAlignIntoTrees(state, store) {
6299
6327
  nodes[key] = override;
6300
6328
  continue;
6301
6329
  }
6302
- row.blocks[blockIdx] = { ...stripChildAligns(block), align: override.align };
6330
+ row.blocks[blockIdx] = { ...authoritative(block), align: override.align };
6303
6331
  const rest = { ...override };
6304
6332
  delete rest.align;
6305
6333
  if (Object.keys(rest).length > 0) nodes[key] = rest;
@@ -6312,7 +6340,7 @@ function foldAlignIntoTrees(state, store) {
6312
6340
  continue;
6313
6341
  }
6314
6342
  for (const row of tree.rows) {
6315
- row.blocks = row.blocks.map((block) => ({ ...stripChildAligns(block), align: override.align }));
6343
+ row.blocks = row.blocks.map((block) => ({ ...authoritative(block), align: override.align }));
6316
6344
  }
6317
6345
  const rest = { ...override };
6318
6346
  delete rest.align;
@@ -10390,7 +10418,12 @@ function AiTreeRenderer({
10390
10418
  // the full row height instead of clumping at the top.
10391
10419
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
10392
10420
  },
10393
- children: renderNode(block, ctx, `r${r2}.b${b}`)
10421
+ children: renderNode(
10422
+ block,
10423
+ ctx,
10424
+ `r${r2}.b${b}`,
10425
+ settings.alignment === "center" ? "center" : "left"
10426
+ )
10394
10427
  },
10395
10428
  b
10396
10429
  ))
@@ -30297,17 +30330,114 @@ function OhhwellsBridge() {
30297
30330
  value = JSON.stringify(foldedHeight.store);
30298
30331
  }
30299
30332
  }
30333
+ const restoreNodes = e.data.restoreNodes;
30334
+ const restored = [];
30335
+ if (restoreNodes && typeof restoreNodes === "object") {
30336
+ for (const [key, html] of Object.entries(restoreNodes)) {
30337
+ if (typeof html !== "string") continue;
30338
+ const els = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
30339
+ if (els.length === 0) continue;
30340
+ for (const el of Array.from(els)) el.innerHTML = html;
30341
+ editContentRef.current = { ...editContentRef.current, [key]: html };
30342
+ restored.push({ key, text: html });
30343
+ }
30344
+ }
30345
+ const previousNodes = {};
30346
+ const strippedNodes = [];
30347
+ if (!restoreNodes) {
30348
+ const aligned = parseStyleStore(value);
30349
+ const prior = parseStyleStore(previous);
30350
+ const keys = /* @__PURE__ */ new Set();
30351
+ if (aligned) {
30352
+ for (const [key, override] of Object.entries(aligned.nodes)) {
30353
+ if (override.align === void 0) continue;
30354
+ if (prior?.nodes[key]?.align === override.align) continue;
30355
+ keys.add(key);
30356
+ }
30357
+ for (const [sectionId, override] of Object.entries(aligned.sections)) {
30358
+ if (override.align === void 0) continue;
30359
+ if (prior?.sections[sectionId]?.align === override.align) continue;
30360
+ const escaped = CSS.escape(sectionId);
30361
+ const markers = document.querySelectorAll(
30362
+ `[data-ohw-instance="${escaped}"], [data-ohw-section="${escaped}"]`
30363
+ );
30364
+ for (const marker of Array.from(markers)) {
30365
+ const editables = marker.querySelectorAll(
30366
+ '[data-ohw-key][data-ohw-editable="text"], [data-ohw-key][data-ohw-editable="plain"]'
30367
+ );
30368
+ for (const el of Array.from(editables)) {
30369
+ const key = el.getAttribute("data-ohw-key");
30370
+ if (key) keys.add(key);
30371
+ }
30372
+ }
30373
+ }
30374
+ }
30375
+ for (const key of keys) {
30376
+ if (key.startsWith(AI_SLOT_KEY_PREFIX) || key.startsWith("__ohw_")) continue;
30377
+ const els = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
30378
+ if (els.length === 0) continue;
30379
+ const before = sanitizeHtml(els[0].innerHTML);
30380
+ const after = stripTextAlignCss(before);
30381
+ if (after === before) continue;
30382
+ for (const el of Array.from(els)) el.innerHTML = after;
30383
+ previousNodes[key] = before;
30384
+ editContentRef.current = { ...editContentRef.current, [key]: after };
30385
+ strippedNodes.push({ key, text: after });
30386
+ }
30387
+ }
30300
30388
  stylesRef.current = value;
30301
30389
  applyStylesToDom(parseStyleStore(value));
30302
- postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
30390
+ postToParentRef.current({
30391
+ type: "ow:change",
30392
+ nodes: [{ key: STYLE_STORE_KEY, text: value }, ...strippedNodes, ...restored]
30393
+ });
30303
30394
  postToParentRef.current({
30304
30395
  type: "ow:ai-styles-applied",
30305
30396
  previous,
30306
30397
  value,
30307
- ...previousSections !== void 0 ? { previousSections } : {}
30398
+ ...previousSections !== void 0 ? { previousSections } : {},
30399
+ ...Object.keys(previousNodes).length > 0 ? { previousNodes } : {}
30308
30400
  });
30309
30401
  };
30310
30402
  window.addEventListener("message", handleAiSetStyles);
30403
+ const handleAiSetButtonStyle = (e) => {
30404
+ if (e.data?.type !== "ow:ai-set-button-style") return;
30405
+ const nodeKey = typeof e.data.hrefKey === "string" ? e.data.hrefKey : "";
30406
+ const keyed = nodeKey ? document.querySelector(
30407
+ `[data-ohw-href-key="${CSS.escape(nodeKey)}"], [data-ohw-key="${CSS.escape(nodeKey)}"]`
30408
+ ) : null;
30409
+ const hrefKey = buttonId(keyed);
30410
+ const el = hrefKey ? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) : null;
30411
+ const previous = editContentRef.current[BUTTON_ASSIGNMENTS_KEY] ?? "";
30412
+ if (!el || !hrefKey || !isCtaButton(el)) {
30413
+ postToParentRef.current({ type: "ow:ai-button-style-applied", ok: false, previous, value: previous });
30414
+ return;
30415
+ }
30416
+ const tokens = tokensFromButton(el);
30417
+ const current = assignmentFor(el, editContentRef.current, tokens);
30418
+ const patch = e.data.assignment;
30419
+ const next = {
30420
+ variant: patch?.variant ?? current.variant,
30421
+ size: patch?.size ?? current.size
30422
+ };
30423
+ const value = typeof e.data.assignments === "string" ? e.data.assignments : JSON.stringify(withAssignment(editContentRef.current, hrefKey, next));
30424
+ editContentRef.current = { ...editContentRef.current, [BUTTON_ASSIGNMENTS_KEY]: value };
30425
+ applyButtonStyle(el, next, tokens);
30426
+ postToParentRef.current({
30427
+ type: "ow:change",
30428
+ nodes: [{ key: BUTTON_ASSIGNMENTS_KEY, text: value }]
30429
+ });
30430
+ postToParentRef.current({
30431
+ type: "ow:ai-button-style-applied",
30432
+ ok: true,
30433
+ // The resolved id, which may differ from the key the editor sent — undo must target this one.
30434
+ hrefKey,
30435
+ previous,
30436
+ previousAssignment: current,
30437
+ value
30438
+ });
30439
+ };
30440
+ window.addEventListener("message", handleAiSetButtonStyle);
30311
30441
  const handleGetBrand = (e) => {
30312
30442
  if (e.data?.type !== "ow:get-brand") return;
30313
30443
  const template = deriveTemplateBrand();
@@ -31347,6 +31477,7 @@ function OhhwellsBridge() {
31347
31477
  window.removeEventListener("message", handleAiSetSections);
31348
31478
  window.removeEventListener("message", handleAiSetBrand);
31349
31479
  window.removeEventListener("message", handleAiSetStyles);
31480
+ window.removeEventListener("message", handleAiSetButtonStyle);
31350
31481
  window.removeEventListener("message", handleCollectItems);
31351
31482
  window.removeEventListener("message", handleAiApplyItems);
31352
31483
  window.removeEventListener("message", handleGetBrand);
@@ -32886,6 +33017,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
32886
33017
  resolveFooterCopyright,
32887
33018
  serializeAiFooterState,
32888
33019
  stepViableSlide,
33020
+ stripTextAlignCss,
32889
33021
  toggleVariants,
32890
33022
  useOhwCarousel,
32891
33023
  validateUrlInput,