@ohhwells/bridge 0.1.51-next.119 → 0.1.51-next.120
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/README.md +30 -1
- package/dist/index.cjs +66 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +66 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -344,11 +344,40 @@ import { LinkPopover } from "@ohhwells/bridge";
|
|
|
344
344
|
The iframe bridge reports page sections on `ow:ready`:
|
|
345
345
|
|
|
346
346
|
```json
|
|
347
|
-
{ "type": "ow:ready", "version": "1", "nodes": [...], "sections": [{ "id": "hero", "label": "Hero" }] }
|
|
347
|
+
{ "type": "ow:ready", "version": "1", "bridgeVersion": "0.1.50", "nodes": [...], "sections": [{ "id": "hero", "label": "Hero" }] }
|
|
348
348
|
```
|
|
349
349
|
|
|
350
|
+
`version` is the protocol version; `bridgeVersion` is the npm package version (inlined at build time), letting the editor detect sites running an older bridge and gate version-dependent features (e.g. AI prompting) gracefully.
|
|
351
|
+
|
|
350
352
|
Templates must mark sections with `data-ohw-section` (and optional `data-ohw-section-label`).
|
|
351
353
|
|
|
354
|
+
### Section-scoped content collection (AI prompt context)
|
|
355
|
+
|
|
356
|
+
The editor can request a single section's editable content (edit mode only):
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
// editor → iframe
|
|
360
|
+
{ "type": "ow:collect-section", "sectionId": "hero" }
|
|
361
|
+
|
|
362
|
+
// iframe → editor
|
|
363
|
+
{ "type": "ow:section-nodes", "sectionId": "hero", "found": true,
|
|
364
|
+
"nodes": [{ "key": "hero-headline", "type": "text/rich", "text": "..." }],
|
|
365
|
+
"rect": { "top": 0, "left": 0, "width": 1280, "height": 720 } }
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
- `found` is `false` (with empty `nodes`, `rect: null`) when no `[data-ohw-section="<id>"]` exists on the current page.
|
|
369
|
+
- `rect` is the section element's document-relative bounding box — the editor uses it to draw the in-flight overlay over just the target section.
|
|
370
|
+
- The returned set contains pure section content only: structural keys (`__ohw_*`), carousel/video-setting/meta nodes, and keyless editables are never included.
|
|
371
|
+
- Node `type` is normalized to the set `image | bg-image | video | link | plain | text/rich | text` (unknown template-declared types become `text`).
|
|
372
|
+
|
|
373
|
+
`ow:enter-edit` (iframe → editor, sent when the user clicks into an editable) also carries the enclosing section so the editor can offer that section as the AI prompt target:
|
|
374
|
+
|
|
375
|
+
```json
|
|
376
|
+
{ "type": "ow:enter-edit", "key": "hero-headline", "sectionId": "hero", "sectionLabel": "Hero" }
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
`sectionId`/`sectionLabel` are `null` for editables outside any `[data-ohw-section]` (label also when `data-ohw-section-label` is absent).
|
|
380
|
+
|
|
352
381
|
### Local link-editor testing (dev fixtures)
|
|
353
382
|
|
|
354
383
|
Without the canvas editor parent, add `ohw-fixtures=1` to the edit URL. This preloads Re:Bound pages and sections into the link popover:
|
package/dist/index.cjs
CHANGED
|
@@ -8717,10 +8717,13 @@ function getLinkHref3(el) {
|
|
|
8717
8717
|
const anchor = el instanceof HTMLAnchorElement ? el : el.querySelector("a");
|
|
8718
8718
|
return anchor?.getAttribute("href") ?? "";
|
|
8719
8719
|
}
|
|
8720
|
-
function collectEditableNodes(extraContent) {
|
|
8721
|
-
const
|
|
8722
|
-
|
|
8723
|
-
|
|
8720
|
+
function collectEditableNodes(extraContent, root = document) {
|
|
8721
|
+
const isScoped = root !== document;
|
|
8722
|
+
const editableEls = Array.from(root.querySelectorAll("[data-ohw-editable]"));
|
|
8723
|
+
if (isScoped && root instanceof HTMLElement && root.matches("[data-ohw-editable]")) {
|
|
8724
|
+
editableEls.unshift(root);
|
|
8725
|
+
}
|
|
8726
|
+
const nodes = editableEls.map((el) => {
|
|
8724
8727
|
if (el.dataset.ohwEditable === "image") {
|
|
8725
8728
|
const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
|
|
8726
8729
|
return { key: el.dataset.ohwKey ?? "", type: "image", text: img?.src ?? "" };
|
|
@@ -8742,12 +8745,16 @@ function collectEditableNodes(extraContent) {
|
|
|
8742
8745
|
text: el.dataset.ohwEditable === "plain" ? el.innerText ?? "" : el.innerHTML
|
|
8743
8746
|
};
|
|
8744
8747
|
});
|
|
8745
|
-
|
|
8748
|
+
const hrefEls = Array.from(root.querySelectorAll("[data-ohw-href-key]"));
|
|
8749
|
+
if (isScoped && root instanceof HTMLElement && root.matches("[data-ohw-href-key]")) {
|
|
8750
|
+
hrefEls.unshift(root);
|
|
8751
|
+
}
|
|
8752
|
+
hrefEls.forEach((el) => {
|
|
8746
8753
|
const key = el.getAttribute("data-ohw-href-key") ?? "";
|
|
8747
8754
|
if (!key) return;
|
|
8748
8755
|
nodes.push({ key, type: "link", text: getLinkHref3(el) });
|
|
8749
8756
|
});
|
|
8750
|
-
if (extraContent) {
|
|
8757
|
+
if (extraContent && !isScoped) {
|
|
8751
8758
|
for (const key of [NAV_ORDER_KEY, FOOTER_ORDER_KEY, NAV_COUNT_KEY]) {
|
|
8752
8759
|
const text = extraContent[key];
|
|
8753
8760
|
if (typeof text === "string" && text.length > 0) {
|
|
@@ -8755,30 +8762,32 @@ function collectEditableNodes(extraContent) {
|
|
|
8755
8762
|
}
|
|
8756
8763
|
}
|
|
8757
8764
|
}
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8765
|
+
if (!isScoped) {
|
|
8766
|
+
document.querySelectorAll('[data-ohw-editable="video"]').forEach((el) => {
|
|
8767
|
+
const key = el.dataset.ohwKey ?? "";
|
|
8768
|
+
const video = getVideoEl(el);
|
|
8769
|
+
if (!key || !video) return;
|
|
8770
|
+
nodes.push({
|
|
8771
|
+
key: `${key}${VIDEO_AUTOPLAY_SUFFIX}`,
|
|
8772
|
+
type: "video-setting",
|
|
8773
|
+
text: String(video.autoplay)
|
|
8774
|
+
});
|
|
8775
|
+
nodes.push({
|
|
8776
|
+
key: `${key}${VIDEO_MUTED_SUFFIX}`,
|
|
8777
|
+
type: "video-setting",
|
|
8778
|
+
text: String(video.muted)
|
|
8779
|
+
});
|
|
8771
8780
|
});
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8781
|
+
for (const key of listCarouselKeys()) {
|
|
8782
|
+
nodes.push({ key, type: "carousel", text: JSON.stringify(readCarouselValue(key)) });
|
|
8783
|
+
}
|
|
8775
8784
|
}
|
|
8776
8785
|
const byKey = /* @__PURE__ */ new Map();
|
|
8777
8786
|
for (const node of nodes) {
|
|
8778
8787
|
if (!node.key) continue;
|
|
8779
8788
|
if (!byKey.has(node.key)) byKey.set(node.key, node);
|
|
8780
8789
|
}
|
|
8781
|
-
if (extraContent) {
|
|
8790
|
+
if (extraContent && !isScoped) {
|
|
8782
8791
|
applyNavFooterDeleteOverrides(byKey, extraContent);
|
|
8783
8792
|
}
|
|
8784
8793
|
return Array.from(byKey.values());
|
|
@@ -9391,6 +9400,10 @@ function syncNavigationDragCursorAttrs() {
|
|
|
9391
9400
|
el.setAttribute("data-ohw-can-drag", "");
|
|
9392
9401
|
});
|
|
9393
9402
|
}
|
|
9403
|
+
var AI_NODE_TYPES = /* @__PURE__ */ new Set(["image", "bg-image", "video", "link", "plain", "text/rich", "text"]);
|
|
9404
|
+
function normalizeAiNodeType(type) {
|
|
9405
|
+
return AI_NODE_TYPES.has(type) ? type : "text";
|
|
9406
|
+
}
|
|
9394
9407
|
function isMediaEditable(el) {
|
|
9395
9408
|
const t = el.dataset.ohwEditable;
|
|
9396
9409
|
return t === "image" || t === "bg-image" || t === "video";
|
|
@@ -11110,7 +11123,14 @@ function OhhwellsBridge() {
|
|
|
11110
11123
|
setReorderDragDisabled(reorderDisabled);
|
|
11111
11124
|
}
|
|
11112
11125
|
setToolbarRect(getEditMeasureEl(el).getBoundingClientRect());
|
|
11113
|
-
|
|
11126
|
+
const sectionEl = el.closest("[data-ohw-section]");
|
|
11127
|
+
postToParent2({
|
|
11128
|
+
type: "ow:enter-edit",
|
|
11129
|
+
key: el.dataset.ohwKey,
|
|
11130
|
+
// Section the edited element belongs to, so the editor can scope AI prompts to it.
|
|
11131
|
+
sectionId: sectionEl?.dataset.ohwSection ?? null,
|
|
11132
|
+
sectionLabel: sectionEl?.dataset.ohwSectionLabel ?? null
|
|
11133
|
+
});
|
|
11114
11134
|
requestAnimationFrame(() => refreshActiveCommandsRef.current());
|
|
11115
11135
|
}, [clearSelectedAttr, deactivate, postToParent2]);
|
|
11116
11136
|
activateRef.current = activate;
|
|
@@ -12786,6 +12806,25 @@ function OhhwellsBridge() {
|
|
|
12786
12806
|
const h = document.documentElement.scrollHeight;
|
|
12787
12807
|
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
12788
12808
|
};
|
|
12809
|
+
const handleCollectSection = (e) => {
|
|
12810
|
+
if (e.data?.type !== "ow:collect-section") return;
|
|
12811
|
+
const sectionId = e.data.sectionId;
|
|
12812
|
+
if (typeof sectionId !== "string" || !sectionId) return;
|
|
12813
|
+
const sectionEl = document.querySelector(
|
|
12814
|
+
`[data-ohw-section="${CSS.escape(sectionId)}"]`
|
|
12815
|
+
);
|
|
12816
|
+
const nodes = sectionEl ? collectEditableNodes(void 0, sectionEl).filter(
|
|
12817
|
+
(n) => n.key && !n.key.startsWith("__ohw_") && !["meta", "sections", "carousel", "video-setting"].includes(n.type)
|
|
12818
|
+
).map((n) => ({ ...n, text: n.text ?? "", type: normalizeAiNodeType(n.type) })) : [];
|
|
12819
|
+
const box = sectionEl?.getBoundingClientRect();
|
|
12820
|
+
const rect = box ? {
|
|
12821
|
+
top: box.top + window.scrollY,
|
|
12822
|
+
left: box.left + window.scrollX,
|
|
12823
|
+
width: box.width,
|
|
12824
|
+
height: box.height
|
|
12825
|
+
} : null;
|
|
12826
|
+
postToParentRef.current({ type: "ow:section-nodes", sectionId, found: !!sectionEl, nodes, rect });
|
|
12827
|
+
};
|
|
12789
12828
|
let selectionChangeRaf = null;
|
|
12790
12829
|
const runSelectionChange = () => {
|
|
12791
12830
|
selectionChangeRaf = null;
|
|
@@ -13006,6 +13045,7 @@ function OhhwellsBridge() {
|
|
|
13006
13045
|
window.addEventListener("message", handleScheduleLinked);
|
|
13007
13046
|
window.addEventListener("message", handleClearSchedulingWidget);
|
|
13008
13047
|
window.addEventListener("message", handleRemoveSchedulingSection);
|
|
13048
|
+
window.addEventListener("message", handleCollectSection);
|
|
13009
13049
|
window.addEventListener("message", handleImageUrl);
|
|
13010
13050
|
window.addEventListener("message", handleImageUploading);
|
|
13011
13051
|
window.addEventListener("message", handleCarouselChange);
|
|
@@ -13056,6 +13096,7 @@ function OhhwellsBridge() {
|
|
|
13056
13096
|
window.removeEventListener("message", handleScheduleLinked);
|
|
13057
13097
|
window.removeEventListener("message", handleClearSchedulingWidget);
|
|
13058
13098
|
window.removeEventListener("message", handleRemoveSchedulingSection);
|
|
13099
|
+
window.removeEventListener("message", handleCollectSection);
|
|
13059
13100
|
window.removeEventListener("message", handleImageUrl);
|
|
13060
13101
|
window.removeEventListener("message", handleImageUploading);
|
|
13061
13102
|
window.removeEventListener("message", handleCarouselChange);
|
|
@@ -13268,6 +13309,7 @@ function OhhwellsBridge() {
|
|
|
13268
13309
|
postToParent2({
|
|
13269
13310
|
type: "ow:ready",
|
|
13270
13311
|
version: "1",
|
|
13312
|
+
bridgeVersion: "0.1.51",
|
|
13271
13313
|
path: pathname,
|
|
13272
13314
|
nodes: collectEditableNodes(editContentRef.current),
|
|
13273
13315
|
sections
|