@ohhwells/bridge 0.1.53-next.145 → 0.1.54-next.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.cjs CHANGED
@@ -155,7 +155,8 @@ function parseAiSectionsState(raw) {
155
155
  label: typeof entry.label === "string" ? entry.label : "Generated section",
156
156
  media: entry.media && typeof entry.media === "object" ? entry.media : {}
157
157
  }));
158
- return { v: 1, sections };
158
+ const removed = Array.isArray(parsed.removed) ? parsed.removed.filter((id) => typeof id === "string" && id.length > 0) : [];
159
+ return { v: 1, sections, ...removed.length ? { removed } : {} };
159
160
  } catch {
160
161
  return EMPTY_AI_SECTIONS;
161
162
  }
@@ -164,17 +165,30 @@ function serializeAiSectionsState(state) {
164
165
  return JSON.stringify(state);
165
166
  }
166
167
  function applyTreeToState(state, payload) {
168
+ const insertBefore = payload.mode === "insert" && payload.position === "before";
167
169
  const entry = {
168
170
  id: payload.id,
169
171
  label: payload.label ?? "Generated section",
170
- afterSection: payload.mode === "insert" ? payload.targetSectionId ?? null : null,
172
+ afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
173
+ ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
171
174
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
172
175
  tree: payload.tree,
173
176
  media: payload.media ?? {},
174
177
  ...payload.brand && typeof payload.brand === "object" ? { brand: payload.brand } : {}
175
178
  };
176
179
  const others = state.sections.filter((existing) => existing.id !== entry.id);
177
- return { v: 1, sections: [...others, entry] };
180
+ return { ...state, v: 1, sections: [...others, entry] };
181
+ }
182
+ function removeFromState(state, id) {
183
+ return { ...state, v: 1, sections: state.sections.filter((entry) => entry.id !== id) };
184
+ }
185
+ function deleteSectionFromState(state, sectionId) {
186
+ if (state.sections.some((entry) => entry.id === sectionId)) {
187
+ return removeFromState(state, sectionId);
188
+ }
189
+ const removed = state.removed ?? [];
190
+ if (removed.includes(sectionId)) return state;
191
+ return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
178
192
  }
179
193
 
180
194
  // src/ui/ai-tree/aiSectionsManager.tsx
@@ -425,7 +439,7 @@ function PersonEl({
425
439
  )
426
440
  }
427
441
  ),
428
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { flex: 1, minWidth: 0 }, children: [
429
443
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
430
444
  "span",
431
445
  {
@@ -499,7 +513,14 @@ function PricingCard({ node, ctx, path }) {
499
513
  features && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
500
514
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { style: { border: "none", borderTop: `1px solid ${dark}`, opacity: 0.2, margin: 0, width: "100%" } }),
501
515
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: AI_TREE_TOKENS.spacing4 }, children: [
502
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...typeStyle({ size: 24, line: 1.3, weight: 700 }, ctx.brand.fonts.body), color: dark }, children: "Includes:" }),
516
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
517
+ "div",
518
+ {
519
+ ...textAttrs(ctx, `${path}.featuresLabel`),
520
+ style: { ...typeStyle({ size: 24, line: 1.3, weight: 700 }, ctx.brand.fonts.body), color: dark },
521
+ children: str(slots.featuresLabel) || "Includes:"
522
+ }
523
+ ),
503
524
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: FEATURE_LINE_CSS }),
504
525
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
505
526
  "div",
@@ -1203,6 +1224,7 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1203
1224
  var import_jsx_runtime2 = require("react/jsx-runtime");
1204
1225
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1205
1226
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1227
+ var REMOVED_ATTR = "data-ohw-ai-removed";
1206
1228
  function deriveTemplateBrand() {
1207
1229
  if (typeof document === "undefined") return null;
1208
1230
  const cs = getComputedStyle(document.documentElement);
@@ -1231,6 +1253,14 @@ function findTemplateSection(id) {
1231
1253
  }
1232
1254
  return null;
1233
1255
  }
1256
+ function findPlacementAnchor(id, exclude) {
1257
+ for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
1258
+ if (el === exclude) continue;
1259
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
1260
+ return el;
1261
+ }
1262
+ return null;
1263
+ }
1234
1264
  function placeContainer(container, entry) {
1235
1265
  if (entry.replaces) {
1236
1266
  const original = findTemplateSection(entry.replaces);
@@ -1239,8 +1269,15 @@ function placeContainer(container, entry) {
1239
1269
  return;
1240
1270
  }
1241
1271
  }
1272
+ if (entry.beforeSection) {
1273
+ const anchor = findPlacementAnchor(entry.beforeSection, container);
1274
+ if (anchor) {
1275
+ anchor.insertAdjacentElement("beforebegin", container);
1276
+ return;
1277
+ }
1278
+ }
1242
1279
  if (entry.afterSection) {
1243
- const anchor = findTemplateSection(entry.afterSection);
1280
+ const anchor = findPlacementAnchor(entry.afterSection, container);
1244
1281
  if (anchor) {
1245
1282
  anchor.insertAdjacentElement("afterend", container);
1246
1283
  return;
@@ -1253,6 +1290,23 @@ function placeContainer(container, entry) {
1253
1290
  document.body.appendChild(container);
1254
1291
  }
1255
1292
  }
1293
+ function syncRemovedSections(state) {
1294
+ const removed = new Set(state.removed ?? []);
1295
+ for (const el of document.querySelectorAll(`[${REMOVED_ATTR}]`)) {
1296
+ const id = el.getAttribute("data-ohw-section") ?? "";
1297
+ if (!removed.has(id)) {
1298
+ el.style.removeProperty("display");
1299
+ el.removeAttribute(REMOVED_ATTR);
1300
+ }
1301
+ }
1302
+ for (const id of removed) {
1303
+ const section = findTemplateSection(id);
1304
+ if (section && !section.hasAttribute(REPLACED_ATTR)) {
1305
+ section.style.display = "none";
1306
+ section.setAttribute(REMOVED_ATTR, "");
1307
+ }
1308
+ }
1309
+ }
1256
1310
  function syncReplacedOriginals(state) {
1257
1311
  for (const el of document.querySelectorAll(`[${REPLACED_ATTR}]`)) {
1258
1312
  const byId = el.getAttribute(REPLACED_ATTR) ?? "";
@@ -1317,6 +1371,7 @@ function applyAiSectionsToDom(state, options) {
1317
1371
  mounted.set(entry.id, { root, container, serialized });
1318
1372
  }
1319
1373
  syncReplacedOriginals(state);
1374
+ syncRemovedSections(state);
1320
1375
  }
1321
1376
 
1322
1377
  // src/useLinkHrefGuardian.ts
@@ -11668,9 +11723,12 @@ function collectEditableNodes(extraContent, root = document) {
11668
11723
  text: String(video.muted)
11669
11724
  });
11670
11725
  });
11671
- for (const key of listCarouselKeys()) {
11672
- nodes.push({ key, type: "carousel", text: JSON.stringify(readCarouselValue(key)) });
11673
- }
11726
+ }
11727
+ const carouselKeys = isScoped ? new Set(
11728
+ Array.from(root.querySelectorAll("[data-ohw-carousel]")).map((el) => el.getAttribute("data-ohw-key") ?? "").filter(Boolean)
11729
+ ) : listCarouselKeys();
11730
+ for (const key of carouselKeys) {
11731
+ nodes.push({ key, type: "carousel", text: JSON.stringify(readCarouselValue(key)) });
11674
11732
  }
11675
11733
  const byKey = /* @__PURE__ */ new Map();
11676
11734
  for (const node of nodes) {
@@ -12302,7 +12360,16 @@ function syncNavigationDragCursorAttrs() {
12302
12360
  el.setAttribute("data-ohw-can-drag", "");
12303
12361
  });
12304
12362
  }
12305
- var AI_NODE_TYPES = /* @__PURE__ */ new Set(["image", "bg-image", "video", "link", "plain", "text/rich", "text"]);
12363
+ var AI_NODE_TYPES = /* @__PURE__ */ new Set([
12364
+ "image",
12365
+ "bg-image",
12366
+ "video",
12367
+ "link",
12368
+ "plain",
12369
+ "text/rich",
12370
+ "text",
12371
+ "carousel"
12372
+ ]);
12306
12373
  function normalizeAiNodeType(type) {
12307
12374
  return AI_NODE_TYPES.has(type) ? type : "text";
12308
12375
  }
@@ -16127,6 +16194,24 @@ function OhhwellsBridge() {
16127
16194
  appliedEl?.scrollIntoView({ behavior: "smooth", block: "center" });
16128
16195
  };
16129
16196
  window.addEventListener("message", handleAiApplyTree);
16197
+ const handleAiDeleteSection = (e) => {
16198
+ if (e.data?.type !== "ow:ai-delete-section") return;
16199
+ const sectionId = typeof e.data.sectionId === "string" ? e.data.sectionId : "";
16200
+ if (!sectionId || sectionId === "navbar" || sectionId === "footer") return;
16201
+ const exists = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
16202
+ if (!exists) return;
16203
+ const previous = aiSectionsRef.current;
16204
+ const nextState = deleteSectionFromState(parseAiSectionsState(previous), sectionId);
16205
+ const nextValue = serializeAiSectionsState(nextState);
16206
+ aiSectionsRef.current = nextValue;
16207
+ applyAiSectionsToDom(nextState);
16208
+ const newHeight = document.documentElement.scrollHeight;
16209
+ if (newHeight > 50) postToParentRef.current({ type: "ow:height", height: newHeight });
16210
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: nextValue }] });
16211
+ postToParentRef.current({ type: "ow:ai-section-deleted", sectionId, previous, value: nextValue });
16212
+ postAiSectionsChanged();
16213
+ };
16214
+ window.addEventListener("message", handleAiDeleteSection);
16130
16215
  const handleAiSetSections = (e) => {
16131
16216
  if (e.data?.type !== "ow:ai-set-sections") return;
16132
16217
  const value = typeof e.data.value === "string" ? e.data.value : "";
@@ -16449,7 +16534,7 @@ function OhhwellsBridge() {
16449
16534
  `[data-ohw-section="${CSS.escape(sectionId)}"]`
16450
16535
  );
16451
16536
  const nodes = sectionEl ? collectEditableNodes(void 0, sectionEl).filter(
16452
- (n) => n.key && !n.key.startsWith("__ohw_") && !["meta", "sections", "carousel", "video-setting"].includes(n.type)
16537
+ (n) => n.key && !n.key.startsWith("__ohw_") && !["meta", "sections", "video-setting"].includes(n.type)
16453
16538
  ).map((n) => ({ ...n, text: n.text ?? "", type: normalizeAiNodeType(n.type) })) : [];
16454
16539
  const box = sectionEl?.getBoundingClientRect();
16455
16540
  const rect = box ? {
@@ -16776,6 +16861,7 @@ function OhhwellsBridge() {
16776
16861
  window.removeEventListener("message", handleViewMode);
16777
16862
  window.removeEventListener("message", handleHydrate);
16778
16863
  window.removeEventListener("message", handleAiApplyTree);
16864
+ window.removeEventListener("message", handleAiDeleteSection);
16779
16865
  window.removeEventListener("message", handleAiSetSections);
16780
16866
  window.removeEventListener("message", handleDeactivate);
16781
16867
  window.removeEventListener("message", handleToastAction);
@@ -16980,7 +17066,7 @@ function OhhwellsBridge() {
16980
17066
  postToParent2({
16981
17067
  type: "ow:ready",
16982
17068
  version: "1",
16983
- bridgeVersion: "0.1.53",
17069
+ bridgeVersion: "0.1.54",
16984
17070
  path: pathname,
16985
17071
  nodes: collectEditableNodes(editContentRef.current),
16986
17072
  sections