@ohhwells/bridge 0.1.146 → 0.1.148

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.d.cts CHANGED
@@ -469,6 +469,13 @@ interface AiSectionsState {
469
469
  }
470
470
  /** Parse a persisted value defensively — malformed state renders nothing, never throws. */
471
471
  declare function parseAiSectionsState(raw: string | null | undefined): AiSectionsState;
472
+ /**
473
+ * Drop every `text-align` declaration from the inline style attributes in one content string,
474
+ * removing a style attribute that carried nothing else. Regex rather than DOMParser so the fold
475
+ * stays pure and usable outside a browser; the only authors of these attributes are the bridge's
476
+ * own `sanitizeHtml` and the model's alignment wraps, which both emit double-quoted attributes.
477
+ */
478
+ declare function stripTextAlignCss(html: string): string;
472
479
  /**
473
480
  * Fold alignment overrides that target GENERATED sections into their trees, where alignment is
474
481
  * first-class data (block.align) that persists with the rest of the tree. A node override
@@ -867,4 +874,4 @@ declare function useOhwCarousel(key: string, initial: CarouselSlide[]): {
867
874
  bind: Record<string, string>;
868
875
  };
869
876
 
870
- export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, foldAlignIntoTrees, getEditModeInitialState, insertFooterHours, isEditSessionActive, isRenderableFooterTree, isRenderableTree, isValidUrl, nextViableSlide, parseAiFooterState, parseAiSectionsState, parseTarget, removeFooterBlock, resolveFooterCopyright, serializeAiFooterState, stepViableSlide, toggleVariants, useOhwCarousel, validateUrlInput, viableSlides };
877
+ export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, foldAlignIntoTrees, getEditModeInitialState, insertFooterHours, isEditSessionActive, isRenderableFooterTree, isRenderableTree, isValidUrl, nextViableSlide, parseAiFooterState, parseAiSectionsState, parseTarget, removeFooterBlock, resolveFooterCopyright, serializeAiFooterState, stepViableSlide, stripTextAlignCss, toggleVariants, useOhwCarousel, validateUrlInput, viableSlides };
package/dist/index.d.ts CHANGED
@@ -469,6 +469,13 @@ interface AiSectionsState {
469
469
  }
470
470
  /** Parse a persisted value defensively — malformed state renders nothing, never throws. */
471
471
  declare function parseAiSectionsState(raw: string | null | undefined): AiSectionsState;
472
+ /**
473
+ * Drop every `text-align` declaration from the inline style attributes in one content string,
474
+ * removing a style attribute that carried nothing else. Regex rather than DOMParser so the fold
475
+ * stays pure and usable outside a browser; the only authors of these attributes are the bridge's
476
+ * own `sanitizeHtml` and the model's alignment wraps, which both emit double-quoted attributes.
477
+ */
478
+ declare function stripTextAlignCss(html: string): string;
472
479
  /**
473
480
  * Fold alignment overrides that target GENERATED sections into their trees, where alignment is
474
481
  * first-class data (block.align) that persists with the rest of the tree. A node override
@@ -867,4 +874,4 @@ declare function useOhwCarousel(key: string, initial: CarouselSlide[]): {
867
874
  bind: Record<string, string>;
868
875
  };
869
876
 
870
- export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, foldAlignIntoTrees, getEditModeInitialState, insertFooterHours, isEditSessionActive, isRenderableFooterTree, isRenderableTree, isValidUrl, nextViableSlide, parseAiFooterState, parseAiSectionsState, parseTarget, removeFooterBlock, resolveFooterCopyright, serializeAiFooterState, stepViableSlide, toggleVariants, useOhwCarousel, validateUrlInput, viableSlides };
877
+ export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, foldAlignIntoTrees, getEditModeInitialState, insertFooterHours, isEditSessionActive, isRenderableFooterTree, isRenderableTree, isValidUrl, nextViableSlide, parseAiFooterState, parseAiSectionsState, parseTarget, removeFooterBlock, resolveFooterCopyright, serializeAiFooterState, stepViableSlide, stripTextAlignCss, toggleVariants, useOhwCarousel, validateUrlInput, viableSlides };
package/dist/index.js CHANGED
@@ -1476,7 +1476,19 @@ function brandTrackedFontFamily(doc, stack) {
1476
1476
  }
1477
1477
  var paintLockByEl = /* @__PURE__ */ new WeakMap();
1478
1478
  var paintObserverByEl = /* @__PURE__ */ new WeakMap();
1479
- var paintingButton = false;
1479
+ var PAINT_GUARD_ATTRS = {
1480
+ attributes: true,
1481
+ attributeFilter: ["style", "class"]
1482
+ };
1483
+ function paintGuarded(el, assignment, tokens) {
1484
+ const observer = paintObserverByEl.get(el);
1485
+ observer?.disconnect();
1486
+ try {
1487
+ paintButtonChrome(el, assignment, tokens);
1488
+ } finally {
1489
+ observer?.observe(el, PAINT_GUARD_ATTRS);
1490
+ }
1491
+ }
1480
1492
  function paintButtonChrome(el, assignment, tokens) {
1481
1493
  const origin = captureButtonOrigin(el);
1482
1494
  const chrome = tokens.variants[assignment.variant];
@@ -1515,14 +1527,11 @@ function paintButtonChrome(el, assignment, tokens) {
1515
1527
  function guardTemplateHover(el) {
1516
1528
  if (paintObserverByEl.has(el)) return;
1517
1529
  const observer = new MutationObserver(() => {
1518
- if (paintingButton) return;
1519
1530
  const lock = paintLockByEl.get(el);
1520
1531
  if (!lock) return;
1521
- paintingButton = true;
1522
- paintButtonChrome(el, lock.assignment, lock.tokens);
1523
- paintingButton = false;
1532
+ paintGuarded(el, lock.assignment, lock.tokens);
1524
1533
  });
1525
- observer.observe(el, { attributes: true, attributeFilter: ["style", "class"] });
1534
+ observer.observe(el, PAINT_GUARD_ATTRS);
1526
1535
  paintObserverByEl.set(el, observer);
1527
1536
  }
1528
1537
  function paintedTokensOf(el) {
@@ -1539,10 +1548,8 @@ function applyButtonStyle(el, assignment, tokens) {
1539
1548
  }
1540
1549
  };
1541
1550
  paintLockByEl.set(el, { assignment, tokens: tracked });
1542
- paintingButton = true;
1543
- paintButtonChrome(el, assignment, tracked);
1544
- paintingButton = false;
1545
1551
  guardTemplateHover(el);
1552
+ paintGuarded(el, assignment, tracked);
1546
1553
  }
1547
1554
  function applyButtonStylesFromContent(doc, content) {
1548
1555
  const assignments = readAssignments(content);
@@ -6172,6 +6179,26 @@ function applyTreeToState(state, payload) {
6172
6179
  const others = state.sections.filter((existing) => existing.id !== entry.id);
6173
6180
  return { ...state, v: 1, sections: [...others, entry] };
6174
6181
  }
6182
+ function stripTextAlignCss(html) {
6183
+ return html.replace(/\sstyle="([^"]*)"/gi, (whole, value) => {
6184
+ if (!/text-align\s*:/i.test(value)) return whole;
6185
+ const kept = value.split(";").map((decl) => decl.trim()).filter((decl) => decl && !/^text-align\s*:/i.test(decl)).join("; ");
6186
+ return kept ? ` style="${kept}"` : "";
6187
+ });
6188
+ }
6189
+ function stripTextAlignInValue(value) {
6190
+ if (typeof value === "string") return stripTextAlignCss(value);
6191
+ if (Array.isArray(value)) return value.map(stripTextAlignInValue);
6192
+ if (value && typeof value === "object") {
6193
+ return Object.fromEntries(
6194
+ Object.entries(value).map(([key, inner]) => [
6195
+ key,
6196
+ stripTextAlignInValue(inner)
6197
+ ])
6198
+ );
6199
+ }
6200
+ return value;
6201
+ }
6175
6202
  function foldAlignIntoTrees(state, store) {
6176
6203
  const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
6177
6204
  const nextTrees = /* @__PURE__ */ new Map();
@@ -6194,6 +6221,12 @@ function foldAlignIntoTrees(state, store) {
6194
6221
  return stripChildAligns(rest);
6195
6222
  })
6196
6223
  } : node;
6224
+ const stripContentAligns = (node) => ({
6225
+ ...node,
6226
+ ...node.slots ? { slots: stripTextAlignInValue(node.slots) } : {},
6227
+ ...node.children ? { children: node.children.map(stripContentAligns) } : {}
6228
+ });
6229
+ const authoritative = (block) => stripChildAligns(stripContentAligns(block));
6197
6230
  const nodes = {};
6198
6231
  for (const [key, override] of Object.entries(store.nodes)) {
6199
6232
  const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
@@ -6205,7 +6238,7 @@ function foldAlignIntoTrees(state, store) {
6205
6238
  nodes[key] = override;
6206
6239
  continue;
6207
6240
  }
6208
- row.blocks[blockIdx] = { ...stripChildAligns(block), align: override.align };
6241
+ row.blocks[blockIdx] = { ...authoritative(block), align: override.align };
6209
6242
  const rest = { ...override };
6210
6243
  delete rest.align;
6211
6244
  if (Object.keys(rest).length > 0) nodes[key] = rest;
@@ -6218,7 +6251,7 @@ function foldAlignIntoTrees(state, store) {
6218
6251
  continue;
6219
6252
  }
6220
6253
  for (const row of tree.rows) {
6221
- row.blocks = row.blocks.map((block) => ({ ...stripChildAligns(block), align: override.align }));
6254
+ row.blocks = row.blocks.map((block) => ({ ...authoritative(block), align: override.align }));
6222
6255
  }
6223
6256
  const rest = { ...override };
6224
6257
  delete rest.align;
@@ -30124,14 +30157,73 @@ function OhhwellsBridge() {
30124
30157
  value = JSON.stringify(foldedHeight.store);
30125
30158
  }
30126
30159
  }
30160
+ const restoreNodes = e.data.restoreNodes;
30161
+ const restored = [];
30162
+ if (restoreNodes && typeof restoreNodes === "object") {
30163
+ for (const [key, html] of Object.entries(restoreNodes)) {
30164
+ if (typeof html !== "string") continue;
30165
+ const els = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
30166
+ if (els.length === 0) continue;
30167
+ for (const el of Array.from(els)) el.innerHTML = html;
30168
+ editContentRef.current = { ...editContentRef.current, [key]: html };
30169
+ restored.push({ key, text: html });
30170
+ }
30171
+ }
30172
+ const previousNodes = {};
30173
+ const strippedNodes = [];
30174
+ if (!restoreNodes) {
30175
+ const aligned = parseStyleStore(value);
30176
+ const prior = parseStyleStore(previous);
30177
+ const keys = /* @__PURE__ */ new Set();
30178
+ if (aligned) {
30179
+ for (const [key, override] of Object.entries(aligned.nodes)) {
30180
+ if (override.align === void 0) continue;
30181
+ if (prior?.nodes[key]?.align === override.align) continue;
30182
+ keys.add(key);
30183
+ }
30184
+ for (const [sectionId, override] of Object.entries(aligned.sections)) {
30185
+ if (override.align === void 0) continue;
30186
+ if (prior?.sections[sectionId]?.align === override.align) continue;
30187
+ const escaped = CSS.escape(sectionId);
30188
+ const markers = document.querySelectorAll(
30189
+ `[data-ohw-instance="${escaped}"], [data-ohw-section="${escaped}"]`
30190
+ );
30191
+ for (const marker of Array.from(markers)) {
30192
+ const editables = marker.querySelectorAll(
30193
+ '[data-ohw-key][data-ohw-editable="text"], [data-ohw-key][data-ohw-editable="plain"]'
30194
+ );
30195
+ for (const el of Array.from(editables)) {
30196
+ const key = el.getAttribute("data-ohw-key");
30197
+ if (key) keys.add(key);
30198
+ }
30199
+ }
30200
+ }
30201
+ }
30202
+ for (const key of keys) {
30203
+ if (key.startsWith(AI_SLOT_KEY_PREFIX) || key.startsWith("__ohw_")) continue;
30204
+ const els = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
30205
+ if (els.length === 0) continue;
30206
+ const before = sanitizeHtml(els[0].innerHTML);
30207
+ const after = stripTextAlignCss(before);
30208
+ if (after === before) continue;
30209
+ for (const el of Array.from(els)) el.innerHTML = after;
30210
+ previousNodes[key] = before;
30211
+ editContentRef.current = { ...editContentRef.current, [key]: after };
30212
+ strippedNodes.push({ key, text: after });
30213
+ }
30214
+ }
30127
30215
  stylesRef.current = value;
30128
30216
  applyStylesToDom(parseStyleStore(value));
30129
- postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
30217
+ postToParentRef.current({
30218
+ type: "ow:change",
30219
+ nodes: [{ key: STYLE_STORE_KEY, text: value }, ...strippedNodes, ...restored]
30220
+ });
30130
30221
  postToParentRef.current({
30131
30222
  type: "ow:ai-styles-applied",
30132
30223
  previous,
30133
30224
  value,
30134
- ...previousSections !== void 0 ? { previousSections } : {}
30225
+ ...previousSections !== void 0 ? { previousSections } : {},
30226
+ ...Object.keys(previousNodes).length > 0 ? { previousNodes } : {}
30135
30227
  });
30136
30228
  };
30137
30229
  window.addEventListener("message", handleAiSetStyles);
@@ -31476,7 +31568,7 @@ function OhhwellsBridge() {
31476
31568
  postToParent2({
31477
31569
  type: "ow:ready",
31478
31570
  version: "1",
31479
- bridgeVersion: "0.1.145",
31571
+ bridgeVersion: "0.1.147",
31480
31572
  path: pathname,
31481
31573
  nodes: collectEditableNodes(editContentRef.current),
31482
31574
  sections
@@ -32706,6 +32798,7 @@ export {
32706
32798
  resolveFooterCopyright,
32707
32799
  serializeAiFooterState,
32708
32800
  stepViableSlide,
32801
+ stripTextAlignCss,
32709
32802
  toggleVariants,
32710
32803
  useOhwCarousel,
32711
32804
  validateUrlInput,