@ohhwells/bridge 0.1.145 → 0.1.146

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
@@ -28,6 +28,14 @@ interface AiBlockNode {
28
28
  align?: 'left' | 'center' | 'right';
29
29
  slots?: Record<string, unknown>;
30
30
  children?: AiBlockNode[];
31
+ /** Stable id for ordered insertion. Omitted on trees saved before slots. */
32
+ ref?: string;
33
+ /** Fixed slot inside a split (`left`/`right`) or grid (`cell-N`). Omit to keep index order. */
34
+ slot?: string;
35
+ /** Unfilled fixed slots on a group. The published page draws none of these. */
36
+ emptySlots?: string[];
37
+ /** Stack wrapped around a slot that was filled a second time. */
38
+ promoted?: true;
31
39
  /** Shape is per-block-type, defined by the backend's `BlockDef.itemFields` (accordion's
32
40
  * question/answer, media-strip's assetId/alt/href/linkTarget, footer link-list/hours/social-row). */
33
41
  items?: Array<Record<string, unknown>>;
package/dist/index.d.ts CHANGED
@@ -28,6 +28,14 @@ interface AiBlockNode {
28
28
  align?: 'left' | 'center' | 'right';
29
29
  slots?: Record<string, unknown>;
30
30
  children?: AiBlockNode[];
31
+ /** Stable id for ordered insertion. Omitted on trees saved before slots. */
32
+ ref?: string;
33
+ /** Fixed slot inside a split (`left`/`right`) or grid (`cell-N`). Omit to keep index order. */
34
+ slot?: string;
35
+ /** Unfilled fixed slots on a group. The published page draws none of these. */
36
+ emptySlots?: string[];
37
+ /** Stack wrapped around a slot that was filled a second time. */
38
+ promoted?: true;
31
39
  /** Shape is per-block-type, defined by the backend's `BlockDef.itemFields` (accordion's
32
40
  * question/answer, media-strip's assetId/alt/href/linkTarget, footer link-list/hours/social-row). */
33
41
  items?: Array<Record<string, unknown>>;
package/dist/index.js CHANGED
@@ -6164,7 +6164,9 @@ function applyTreeToState(state, payload) {
6164
6164
  ...selfEdit ? priorEntry?.beforeSection ? { beforeSection: priorEntry.beforeSection } : {} : insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
6165
6165
  ...selfEdit ? priorEntry?.replaces ? { replaces: priorEntry.replaces } : {} : payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
6166
6166
  tree: payload.tree,
6167
- media: payload.media ?? {},
6167
+ // A column insert rebuilds the media map from the live DOM keys. The photo
6168
+ // still points at the ref it was generated with. Keep that ref.
6169
+ media: { ...priorEntry?.media ?? {}, ...payload.media ?? {} },
6168
6170
  ...payload.brand && typeof payload.brand === "object" ? { brand: payload.brand } : {}
6169
6171
  };
6170
6172
  const others = state.sections.filter((existing) => existing.id !== entry.id);
@@ -9665,6 +9667,46 @@ function FooterSocialRowBlock({
9665
9667
  }
9666
9668
  );
9667
9669
  }
9670
+ function EmptySlotPlaceholder({ name }) {
9671
+ return /* @__PURE__ */ jsx(
9672
+ "div",
9673
+ {
9674
+ "data-ai-slot": name,
9675
+ "data-ai-slot-empty": "",
9676
+ "aria-hidden": "true",
9677
+ style: {
9678
+ minHeight: 88,
9679
+ borderRadius: 12,
9680
+ border: "1px dashed #D6D3D1",
9681
+ display: "flex",
9682
+ alignItems: "center",
9683
+ justifyContent: "center",
9684
+ padding: 16,
9685
+ color: "#A8A29E",
9686
+ fontSize: 13,
9687
+ lineHeight: 1.4,
9688
+ textAlign: "center",
9689
+ pointerEvents: "none"
9690
+ },
9691
+ children: "Ask AI to add something here"
9692
+ }
9693
+ );
9694
+ }
9695
+ function splitSides(node) {
9696
+ const named = /* @__PURE__ */ new Map();
9697
+ const unnamed = [];
9698
+ (node.children ?? []).forEach((child, index) => {
9699
+ if (child.slot === "left" || child.slot === "right") {
9700
+ named.set(child.slot, { node: child, index });
9701
+ } else {
9702
+ unnamed.push({ node: child, index });
9703
+ }
9704
+ });
9705
+ return {
9706
+ left: named.get("left") ?? unnamed.shift() ?? null,
9707
+ right: named.get("right") ?? unnamed.shift() ?? null
9708
+ };
9709
+ }
9668
9710
  function renderNode(node, ctx, path, ambientAlign) {
9669
9711
  const slots = node.slots ?? {};
9670
9712
  switch (node.type) {
@@ -9672,9 +9714,11 @@ function renderNode(node, ctx, path, ambientAlign) {
9672
9714
  return /* @__PURE__ */ jsx(TextBlock, { slots, ctx, path, align: node.align ?? ambientAlign });
9673
9715
  // Layout container: arranges child blocks, contributes no content of its own. `grid` is a
9674
9716
  // nested 12-column grid the children span (a collage is 3–5 media on it, bottom-aligned so
9675
- // mixed aspects read as a composition); `split` is exactly two children at a ratio; `stack`
9676
- // is a column. Children render through this same dispatcher, so edit markers, media
9677
- // resolution, and copy paths all work unchanged inside a group.
9717
+ // mixed aspects read as a composition); `split` is left and right (index order when `slot`
9718
+ // is omitted); `stack` is a column. A missing split side is a canvas placeholder only when
9719
+ // `emptySlots` names it. A one-child split used to keep a row at span 12 (a testimonial
9720
+ // under a heading) sits centered at half the row width. Children render through this same
9721
+ // dispatcher, so edit markers, media resolution, and copy paths all work unchanged.
9678
9722
  case "group": {
9679
9723
  const layout = str(slots.layout);
9680
9724
  const gap = slots.spacing === "tight" ? AI_TREE_TOKENS.spacing3 : slots.spacing === "airy" ? AI_TREE_TOKENS.spacing8 : AI_TREE_TOKENS.spacing6;
@@ -9772,24 +9816,38 @@ function renderNode(node, ctx, path, ambientAlign) {
9772
9816
  i
9773
9817
  ));
9774
9818
  if (layout === "grid") {
9775
- return /* @__PURE__ */ jsx(
9819
+ const holes = ctx.keyFor ? node.emptySlots ?? [] : [];
9820
+ return /* @__PURE__ */ jsxs(
9776
9821
  "div",
9777
9822
  {
9778
9823
  "data-ai-group": "grid",
9779
9824
  style: { display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap, alignItems: "end" },
9780
- children: kids
9825
+ children: [
9826
+ kids,
9827
+ holes.map((name) => /* @__PURE__ */ jsx("div", { style: { gridColumn: "span 6", minWidth: 0 }, children: /* @__PURE__ */ jsx(EmptySlotPlaceholder, { name }) }, name))
9828
+ ]
9781
9829
  }
9782
9830
  );
9783
9831
  }
9784
9832
  if (layout === "split") {
9833
+ const { left, right } = splitSides(node);
9834
+ const open = new Set(ctx.keyFor ? node.emptySlots ?? [] : []);
9835
+ const side = (entry, name) => entry ? /* @__PURE__ */ jsx("div", { style: { minWidth: 0 }, children: renderNode(entry.node, ctx, `${path}.c${entry.index}`) }, name) : open.has(name) ? /* @__PURE__ */ jsx(EmptySlotPlaceholder, { name }, name) : null;
9836
+ if (!left && !open.has("left") || !right && !open.has("right")) {
9837
+ const only = left ?? right;
9838
+ return only ? /* @__PURE__ */ jsx("div", { "data-ai-group": "split", style: { display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ jsx("div", { style: { width: "50%", minWidth: 0 }, children: renderNode(only.node, ctx, `${path}.c${only.index}`) }) }) : null;
9839
+ }
9785
9840
  const ratio = str(slots.ratio);
9786
9841
  const cols = ratio === "3:5" ? "3fr 5fr" : ratio === "5:3" ? "5fr 3fr" : "1fr 1fr";
9787
- return /* @__PURE__ */ jsx(
9842
+ return /* @__PURE__ */ jsxs(
9788
9843
  "div",
9789
9844
  {
9790
9845
  "data-ai-group": "split",
9791
9846
  style: { display: "grid", gridTemplateColumns: cols, gap, alignItems: "center" },
9792
- children: kids
9847
+ children: [
9848
+ side(left, "left"),
9849
+ side(right, "right")
9850
+ ]
9793
9851
  }
9794
9852
  );
9795
9853
  }
@@ -31418,7 +31476,7 @@ function OhhwellsBridge() {
31418
31476
  postToParent2({
31419
31477
  type: "ow:ready",
31420
31478
  version: "1",
31421
- bridgeVersion: "0.1.144",
31479
+ bridgeVersion: "0.1.145",
31422
31480
  path: pathname,
31423
31481
  nodes: collectEditableNodes(editContentRef.current),
31424
31482
  sections