@ohhwells/bridge 0.1.91 → 0.1.92

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
@@ -24,6 +24,8 @@ type AiBlockType = 'text' | 'button' | 'media' | 'media-list' | 'rating' | 'divi
24
24
  interface AiBlockNode {
25
25
  type: AiBlockType;
26
26
  span: number;
27
+ /** Horizontal alignment of the block's content within its column. */
28
+ align?: 'left' | 'center' | 'right';
27
29
  slots?: Record<string, unknown>;
28
30
  children?: AiBlockNode[];
29
31
  items?: Array<{
@@ -55,8 +57,10 @@ interface AiLayoutTree {
55
57
  scaffold: SectionScaffold;
56
58
  tag?: string;
57
59
  settings?: AiSectionSettings;
60
+ /** A row's `align` sets the vertical alignment of its columns; `stretch` equalises heights. */
58
61
  rows: Array<{
59
62
  blocks: AiBlockNode[];
63
+ align?: 'top' | 'center' | 'bottom' | 'stretch';
60
64
  }>;
61
65
  }
62
66
  /** The site brand kit the renderer styles from (editor Brand settings). */
package/dist/index.d.ts CHANGED
@@ -24,6 +24,8 @@ type AiBlockType = 'text' | 'button' | 'media' | 'media-list' | 'rating' | 'divi
24
24
  interface AiBlockNode {
25
25
  type: AiBlockType;
26
26
  span: number;
27
+ /** Horizontal alignment of the block's content within its column. */
28
+ align?: 'left' | 'center' | 'right';
27
29
  slots?: Record<string, unknown>;
28
30
  children?: AiBlockNode[];
29
31
  items?: Array<{
@@ -55,8 +57,10 @@ interface AiLayoutTree {
55
57
  scaffold: SectionScaffold;
56
58
  tag?: string;
57
59
  settings?: AiSectionSettings;
60
+ /** A row's `align` sets the vertical alignment of its columns; `stretch` equalises heights. */
58
61
  rows: Array<{
59
62
  blocks: AiBlockNode[];
63
+ align?: 'top' | 'center' | 'bottom' | 'stretch';
60
64
  }>;
61
65
  }
62
66
  /** The site brand kit the renderer styles from (editor Brand settings). */
package/dist/index.js CHANGED
@@ -69,6 +69,7 @@ function isRenderableTree(value) {
69
69
 
70
70
  // src/lib/ai-sections-store.ts
71
71
  var AI_SECTIONS_KEY = "__ohw_ai_sections";
72
+ var AI_SLOT_KEY_PREFIX = "ai.";
72
73
  var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
73
74
  function parseAiSectionsState(raw) {
74
75
  if (!raw) return EMPTY_AI_SECTIONS;
@@ -112,6 +113,63 @@ function applyTreeToState(state, payload) {
112
113
  const others = state.sections.filter((existing) => existing.id !== entry.id);
113
114
  return { ...state, v: 1, sections: [...others, entry] };
114
115
  }
116
+ function foldAlignIntoTrees(state, store) {
117
+ const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
118
+ const nextTrees = /* @__PURE__ */ new Map();
119
+ const treeFor = (id) => {
120
+ const cloned = nextTrees.get(id);
121
+ if (cloned) return cloned;
122
+ const entry = byId.get(id);
123
+ if (!entry) return void 0;
124
+ const fresh = {
125
+ ...entry.tree,
126
+ rows: entry.tree.rows.map((row) => ({ ...row, blocks: row.blocks.map((block) => ({ ...block })) }))
127
+ };
128
+ nextTrees.set(id, fresh);
129
+ return fresh;
130
+ };
131
+ const nodes = {};
132
+ for (const [key, override] of Object.entries(store.nodes)) {
133
+ const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
134
+ const tree = match ? treeFor(match[1]) : void 0;
135
+ const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
136
+ if (!block) {
137
+ nodes[key] = override;
138
+ continue;
139
+ }
140
+ block.align = override.align;
141
+ const rest = { ...override };
142
+ delete rest.align;
143
+ if (Object.keys(rest).length > 0) nodes[key] = rest;
144
+ }
145
+ const sections = {};
146
+ for (const [sectionId, override] of Object.entries(store.sections)) {
147
+ const tree = override.align !== void 0 ? treeFor(sectionId) : void 0;
148
+ if (!tree) {
149
+ sections[sectionId] = override;
150
+ continue;
151
+ }
152
+ for (const row of tree.rows) {
153
+ for (const block of row.blocks) block.align = override.align;
154
+ }
155
+ const rest = { ...override };
156
+ delete rest.align;
157
+ if (Object.keys(rest).length > 0) sections[sectionId] = rest;
158
+ }
159
+ if (nextTrees.size === 0) return { state, store, changed: false };
160
+ return {
161
+ state: {
162
+ ...state,
163
+ v: 1,
164
+ sections: state.sections.map((entry) => {
165
+ const tree = nextTrees.get(entry.id);
166
+ return tree ? { ...entry, tree } : entry;
167
+ })
168
+ },
169
+ store: { v: 1, sections, nodes },
170
+ changed: true
171
+ };
172
+ }
115
173
  function removeFromState(state, id) {
116
174
  return { ...state, v: 1, sections: state.sections.filter((entry) => entry.id !== id) };
117
175
  }
@@ -330,6 +388,9 @@ function styleSheetCss() {
330
388
  `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
331
389
  );
332
390
  }
391
+ for (const align of ["left", "center", "right"]) {
392
+ rules.push(`[data-ohw-style-align="${align}"] { text-align: ${align} !important; }`);
393
+ }
333
394
  return rules.join("\n");
334
395
  }
335
396
  var STYLE_FONT_LINK_ID = "ohw-style-fonts";
@@ -356,10 +417,24 @@ var SECTION_ATTRS = {
356
417
  textDistribution: "data-ohw-style-distribution",
357
418
  headlineScale: "data-ohw-style-headline",
358
419
  imageAspect: "data-ohw-style-aspect",
359
- spacing: "data-ohw-style-spacing"
420
+ spacing: "data-ohw-style-spacing",
421
+ align: "data-ohw-style-align"
360
422
  };
361
423
  var NODE_WROTE_ATTR = "data-ohw-style-node";
362
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
424
+ var NODE_PROPS = [
425
+ "color",
426
+ "font-family",
427
+ "font-size",
428
+ "background",
429
+ "text-align",
430
+ "justify-content",
431
+ "align-items"
432
+ ];
433
+ var ALIGN_JUSTIFY = {
434
+ left: "flex-start",
435
+ center: "center",
436
+ right: "flex-end"
437
+ };
363
438
  function saveInline(el, prop) {
364
439
  const attr = `data-ohw-style-prev-${prop}`;
365
440
  if (el.hasAttribute(attr)) return;
@@ -403,6 +478,10 @@ function clearNodeProps(root) {
403
478
  function buttonSurfaceOf(el) {
404
479
  return el.closest("a, button") ?? el;
405
480
  }
481
+ function alignSubjectOf(el) {
482
+ const button = el.closest('[data-ohw-role="button"]');
483
+ return button?.parentElement ?? el;
484
+ }
406
485
  function applyStylesToDom(store) {
407
486
  ensureStyleSheet();
408
487
  clearSectionAttrs(document);
@@ -448,6 +527,18 @@ function applyStylesToDom(store) {
448
527
  el.style.setProperty("font-size", `${override.fontSize}px`, "important");
449
528
  el.setAttribute(NODE_WROTE_ATTR, "");
450
529
  }
530
+ if (override.align !== void 0) {
531
+ const subject = alignSubjectOf(el);
532
+ saveInline(subject, "text-align");
533
+ saveInline(subject, "justify-content");
534
+ subject.style.setProperty("text-align", override.align, "important");
535
+ subject.style.setProperty(
536
+ "justify-content",
537
+ ALIGN_JUSTIFY[override.align] ?? "flex-start",
538
+ "important"
539
+ );
540
+ subject.setAttribute(NODE_WROTE_ATTR, "");
541
+ }
451
542
  if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
452
543
  const surface = buttonSurfaceOf(el);
453
544
  if (override.buttonBackground !== void 0) {
@@ -656,7 +747,7 @@ function TextBlock({ slots, ctx, path }) {
656
747
  }
657
748
  function SectionHeaderBlock({ node, ctx, path }) {
658
749
  const slots = node.slots ?? {};
659
- const align = slots.alignment === "center" ? "center" : "left";
750
+ const align = node.align ?? (slots.alignment === "center" ? "center" : "left");
660
751
  const children = node.children ?? [];
661
752
  const buttonRowIdx = children.findIndex((c) => c.type === "button-row");
662
753
  const buttonRow = buttonRowIdx >= 0 ? children[buttonRowIdx] : void 0;
@@ -700,7 +791,7 @@ function SectionHeaderBlock({ node, ctx, path }) {
700
791
  display: "flex",
701
792
  gap: AI_TREE_TOKENS.spacing6,
702
793
  marginTop: AI_TREE_TOKENS.spacing8,
703
- justifyContent: align === "center" ? "center" : "flex-start"
794
+ justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start"
704
795
  },
705
796
  children: (buttonRow.children ?? []).map((button, i) => /* @__PURE__ */ jsx(
706
797
  ButtonEl,
@@ -1033,7 +1124,7 @@ function CardBlock({ node, ctx, path }) {
1033
1124
  editPath: `${path}.media`
1034
1125
  }
1035
1126
  ) : null;
1036
- const centered = slots.alignment === "center";
1127
+ const centered = (node.align ?? slots.alignment) === "center";
1037
1128
  const content = /* @__PURE__ */ jsxs(
1038
1129
  "div",
1039
1130
  {
@@ -1752,6 +1843,19 @@ function AiTreeRenderer({
1752
1843
  }
1753
1844
  })();
1754
1845
  const distributed = !isOverlay && settings.textDistribution;
1846
+ const rowAlignItems = (rowAlign) => {
1847
+ if (rowAlign === "top") return "start";
1848
+ if (rowAlign === "bottom") return "end";
1849
+ if (rowAlign === "center" || rowAlign === "stretch") return rowAlign;
1850
+ if (distributed === "space-between") return "stretch";
1851
+ return (distributed ?? settings.verticalPosition) === "top" ? "start" : "center";
1852
+ };
1853
+ const cellAlignStyle = (blockAlign) => blockAlign ? {
1854
+ display: "flex",
1855
+ flexDirection: "column",
1856
+ alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
1857
+ textAlign: blockAlign
1858
+ } : {};
1755
1859
  return /* @__PURE__ */ jsxs(
1756
1860
  "section",
1757
1861
  {
@@ -1788,7 +1892,7 @@ function AiTreeRenderer({
1788
1892
  display: "grid",
1789
1893
  gridTemplateColumns: "repeat(12, 1fr)",
1790
1894
  gap: AI_TREE_TOKENS.spacing6,
1791
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1895
+ alignItems: rowAlignItems(row.align),
1792
1896
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1793
1897
  },
1794
1898
  children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
@@ -1798,6 +1902,8 @@ function AiTreeRenderer({
1798
1902
  style: {
1799
1903
  gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1800
1904
  minWidth: 0,
1905
+ // Horizontal placement of the block's content within its column.
1906
+ ...cellAlignStyle(block.align),
1801
1907
  // space-between: each column becomes a flex column whose content spreads over
1802
1908
  // the full row height instead of clumping at the top.
1803
1909
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
@@ -20257,12 +20363,35 @@ function OhhwellsBridge() {
20257
20363
  window.addEventListener("message", handleAiSetBrand);
20258
20364
  const handleAiSetStyles = (e) => {
20259
20365
  if (e.data?.type !== "ow:ai-set-styles") return;
20260
- const value = typeof e.data.value === "string" ? e.data.value : "";
20366
+ let value = typeof e.data.value === "string" ? e.data.value : "";
20261
20367
  const previous = stylesRef.current;
20368
+ let previousSections;
20369
+ const store = parseStyleStore(value);
20370
+ if (store) {
20371
+ const folded = foldAlignIntoTrees(parseAiSectionsState(aiSectionsRef.current), store);
20372
+ if (folded.changed) {
20373
+ const nextSections = serializeAiSectionsState(folded.state);
20374
+ if (nextSections !== aiSectionsRef.current) {
20375
+ previousSections = aiSectionsRef.current;
20376
+ aiSectionsRef.current = nextSections;
20377
+ applyAiSectionsToDom(folded.state);
20378
+ postToParentRef.current({
20379
+ type: "ow:change",
20380
+ nodes: [{ key: AI_SECTIONS_KEY, text: nextSections }]
20381
+ });
20382
+ }
20383
+ value = JSON.stringify(folded.store);
20384
+ }
20385
+ }
20262
20386
  stylesRef.current = value;
20263
20387
  applyStylesToDom(parseStyleStore(value));
20264
20388
  postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
20265
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
20389
+ postToParentRef.current({
20390
+ type: "ow:ai-styles-applied",
20391
+ previous,
20392
+ value,
20393
+ ...previousSections !== void 0 ? { previousSections } : {}
20394
+ });
20266
20395
  };
20267
20396
  window.addEventListener("message", handleAiSetStyles);
20268
20397
  const handleGetBrand = (e) => {
@@ -21283,7 +21412,7 @@ function OhhwellsBridge() {
21283
21412
  postToParent2({
21284
21413
  type: "ow:ready",
21285
21414
  version: "1",
21286
- bridgeVersion: "0.1.90",
21415
+ bridgeVersion: "0.1.91",
21287
21416
  path: pathname,
21288
21417
  nodes: collectEditableNodes(editContentRef.current),
21289
21418
  sections