@ohhwells/bridge 0.1.91 → 0.1.92-next.269
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 +1179 -398
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -4
- package/dist/index.d.ts +32 -4
- package/dist/index.js +1178 -398
- package/dist/index.js.map +1 -1
- package/dist/pages.cjs +141 -0
- package/dist/pages.cjs.map +1 -0
- package/dist/pages.d.cts +45 -0
- package/dist/pages.d.ts +45 -0
- package/dist/pages.js +107 -0
- package/dist/pages.js.map +1 -0
- package/dist/styles.css +55 -0
- package/package.json +8 -3
package/dist/index.js
CHANGED
|
@@ -69,6 +69,10 @@ 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.";
|
|
73
|
+
function aiSlotKeyPrefixFor(sectionId) {
|
|
74
|
+
return `${AI_SLOT_KEY_PREFIX}${sectionId}.`;
|
|
75
|
+
}
|
|
72
76
|
var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
|
|
73
77
|
function parseAiSectionsState(raw) {
|
|
74
78
|
if (!raw) return EMPTY_AI_SECTIONS;
|
|
@@ -112,6 +116,63 @@ function applyTreeToState(state, payload) {
|
|
|
112
116
|
const others = state.sections.filter((existing) => existing.id !== entry.id);
|
|
113
117
|
return { ...state, v: 1, sections: [...others, entry] };
|
|
114
118
|
}
|
|
119
|
+
function foldAlignIntoTrees(state, store) {
|
|
120
|
+
const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
|
|
121
|
+
const nextTrees = /* @__PURE__ */ new Map();
|
|
122
|
+
const treeFor = (id) => {
|
|
123
|
+
const cloned = nextTrees.get(id);
|
|
124
|
+
if (cloned) return cloned;
|
|
125
|
+
const entry = byId.get(id);
|
|
126
|
+
if (!entry) return void 0;
|
|
127
|
+
const fresh = {
|
|
128
|
+
...entry.tree,
|
|
129
|
+
rows: entry.tree.rows.map((row) => ({ ...row, blocks: row.blocks.map((block) => ({ ...block })) }))
|
|
130
|
+
};
|
|
131
|
+
nextTrees.set(id, fresh);
|
|
132
|
+
return fresh;
|
|
133
|
+
};
|
|
134
|
+
const nodes = {};
|
|
135
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
136
|
+
const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
|
|
137
|
+
const tree = match ? treeFor(match[1]) : void 0;
|
|
138
|
+
const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
|
|
139
|
+
if (!block) {
|
|
140
|
+
nodes[key] = override;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
block.align = override.align;
|
|
144
|
+
const rest = { ...override };
|
|
145
|
+
delete rest.align;
|
|
146
|
+
if (Object.keys(rest).length > 0) nodes[key] = rest;
|
|
147
|
+
}
|
|
148
|
+
const sections = {};
|
|
149
|
+
for (const [sectionId, override] of Object.entries(store.sections)) {
|
|
150
|
+
const tree = override.align !== void 0 ? treeFor(sectionId) : void 0;
|
|
151
|
+
if (!tree) {
|
|
152
|
+
sections[sectionId] = override;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
for (const row of tree.rows) {
|
|
156
|
+
for (const block of row.blocks) block.align = override.align;
|
|
157
|
+
}
|
|
158
|
+
const rest = { ...override };
|
|
159
|
+
delete rest.align;
|
|
160
|
+
if (Object.keys(rest).length > 0) sections[sectionId] = rest;
|
|
161
|
+
}
|
|
162
|
+
if (nextTrees.size === 0) return { state, store, changed: false };
|
|
163
|
+
return {
|
|
164
|
+
state: {
|
|
165
|
+
...state,
|
|
166
|
+
v: 1,
|
|
167
|
+
sections: state.sections.map((entry) => {
|
|
168
|
+
const tree = nextTrees.get(entry.id);
|
|
169
|
+
return tree ? { ...entry, tree } : entry;
|
|
170
|
+
})
|
|
171
|
+
},
|
|
172
|
+
store: { v: 1, sections, nodes },
|
|
173
|
+
changed: true
|
|
174
|
+
};
|
|
175
|
+
}
|
|
115
176
|
function removeFromState(state, id) {
|
|
116
177
|
return { ...state, v: 1, sections: state.sections.filter((entry) => entry.id !== id) };
|
|
117
178
|
}
|
|
@@ -123,6 +184,33 @@ function deleteSectionFromState(state, sectionId) {
|
|
|
123
184
|
if (removed.includes(sectionId)) return state;
|
|
124
185
|
return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
|
|
125
186
|
}
|
|
187
|
+
function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE__ */ new Set()) {
|
|
188
|
+
const generated = new Set(state.sections.map((entry) => entry.id));
|
|
189
|
+
const reapedIds = [...new Set(removedIds)].filter((id) => generated.has(id) && !excludeIds.has(id));
|
|
190
|
+
if (reapedIds.length === 0) {
|
|
191
|
+
return { state, store, reapedIds: [], slotPrefixes: [], changed: false };
|
|
192
|
+
}
|
|
193
|
+
const reaped = new Set(reapedIds);
|
|
194
|
+
const slotPrefixes = reapedIds.map(aiSlotKeyPrefixFor);
|
|
195
|
+
const nextState = {
|
|
196
|
+
...state,
|
|
197
|
+
v: 1,
|
|
198
|
+
sections: state.sections.filter((entry) => !reaped.has(entry.id))
|
|
199
|
+
};
|
|
200
|
+
let nextStore = store;
|
|
201
|
+
if (store) {
|
|
202
|
+
const sections = {};
|
|
203
|
+
for (const [key, override] of Object.entries(store.sections)) {
|
|
204
|
+
if (!reaped.has(key)) sections[key] = override;
|
|
205
|
+
}
|
|
206
|
+
const nodes = {};
|
|
207
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
208
|
+
if (!slotPrefixes.some((prefix) => key.startsWith(prefix))) nodes[key] = override;
|
|
209
|
+
}
|
|
210
|
+
nextStore = { v: 1, sections, nodes };
|
|
211
|
+
}
|
|
212
|
+
return { state: nextState, store: nextStore, reapedIds, slotPrefixes, changed: true };
|
|
213
|
+
}
|
|
126
214
|
|
|
127
215
|
// src/lib/brand-chrome.ts
|
|
128
216
|
var BRAND_NAME_KEY = "__ohw_brand_name";
|
|
@@ -330,6 +418,13 @@ function styleSheetCss() {
|
|
|
330
418
|
`[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
|
|
331
419
|
);
|
|
332
420
|
}
|
|
421
|
+
rules.push(
|
|
422
|
+
`[data-ohw-style-corners="sharp"] :is(.card, [data-ohw-card], img, picture, figure, [data-ohw-editable="bg-image"]) { border-radius: 0 !important; }`,
|
|
423
|
+
`[data-ohw-style-corners="sharp"] :has(> img) { border-radius: 0 !important; }`
|
|
424
|
+
);
|
|
425
|
+
for (const align of ["left", "center", "right"]) {
|
|
426
|
+
rules.push(`[data-ohw-style-align="${align}"] { text-align: ${align} !important; }`);
|
|
427
|
+
}
|
|
333
428
|
return rules.join("\n");
|
|
334
429
|
}
|
|
335
430
|
var STYLE_FONT_LINK_ID = "ohw-style-fonts";
|
|
@@ -356,10 +451,25 @@ var SECTION_ATTRS = {
|
|
|
356
451
|
textDistribution: "data-ohw-style-distribution",
|
|
357
452
|
headlineScale: "data-ohw-style-headline",
|
|
358
453
|
imageAspect: "data-ohw-style-aspect",
|
|
359
|
-
spacing: "data-ohw-style-spacing"
|
|
454
|
+
spacing: "data-ohw-style-spacing",
|
|
455
|
+
cornerStyle: "data-ohw-style-corners",
|
|
456
|
+
align: "data-ohw-style-align"
|
|
360
457
|
};
|
|
361
458
|
var NODE_WROTE_ATTR = "data-ohw-style-node";
|
|
362
|
-
var NODE_PROPS = [
|
|
459
|
+
var NODE_PROPS = [
|
|
460
|
+
"color",
|
|
461
|
+
"font-family",
|
|
462
|
+
"font-size",
|
|
463
|
+
"background",
|
|
464
|
+
"text-align",
|
|
465
|
+
"justify-content",
|
|
466
|
+
"align-items"
|
|
467
|
+
];
|
|
468
|
+
var ALIGN_JUSTIFY = {
|
|
469
|
+
left: "flex-start",
|
|
470
|
+
center: "center",
|
|
471
|
+
right: "flex-end"
|
|
472
|
+
};
|
|
363
473
|
function saveInline(el, prop) {
|
|
364
474
|
const attr = `data-ohw-style-prev-${prop}`;
|
|
365
475
|
if (el.hasAttribute(attr)) return;
|
|
@@ -403,6 +513,10 @@ function clearNodeProps(root) {
|
|
|
403
513
|
function buttonSurfaceOf(el) {
|
|
404
514
|
return el.closest("a, button") ?? el;
|
|
405
515
|
}
|
|
516
|
+
function alignSubjectOf(el) {
|
|
517
|
+
const button = el.closest('[data-ohw-role="button"]');
|
|
518
|
+
return button?.parentElement ?? el;
|
|
519
|
+
}
|
|
406
520
|
function applyStylesToDom(store) {
|
|
407
521
|
ensureStyleSheet();
|
|
408
522
|
clearSectionAttrs(document);
|
|
@@ -448,6 +562,18 @@ function applyStylesToDom(store) {
|
|
|
448
562
|
el.style.setProperty("font-size", `${override.fontSize}px`, "important");
|
|
449
563
|
el.setAttribute(NODE_WROTE_ATTR, "");
|
|
450
564
|
}
|
|
565
|
+
if (override.align !== void 0) {
|
|
566
|
+
const subject = alignSubjectOf(el);
|
|
567
|
+
saveInline(subject, "text-align");
|
|
568
|
+
saveInline(subject, "justify-content");
|
|
569
|
+
subject.style.setProperty("text-align", override.align, "important");
|
|
570
|
+
subject.style.setProperty(
|
|
571
|
+
"justify-content",
|
|
572
|
+
ALIGN_JUSTIFY[override.align] ?? "flex-start",
|
|
573
|
+
"important"
|
|
574
|
+
);
|
|
575
|
+
subject.setAttribute(NODE_WROTE_ATTR, "");
|
|
576
|
+
}
|
|
451
577
|
if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
|
|
452
578
|
const surface = buttonSurfaceOf(el);
|
|
453
579
|
if (override.buttonBackground !== void 0) {
|
|
@@ -468,9 +594,535 @@ function applyStylesToDom(store) {
|
|
|
468
594
|
import { flushSync } from "react-dom";
|
|
469
595
|
import { createRoot } from "react-dom/client";
|
|
470
596
|
|
|
597
|
+
// src/lib/sections.ts
|
|
598
|
+
var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
599
|
+
function isChromeSection(el) {
|
|
600
|
+
return el.matches("header, nav, footer, aside");
|
|
601
|
+
}
|
|
602
|
+
function titleCaseSectionId(id) {
|
|
603
|
+
return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
604
|
+
}
|
|
605
|
+
function parseSectionsFromRoot(root) {
|
|
606
|
+
const seen = /* @__PURE__ */ new Set();
|
|
607
|
+
const sections = [];
|
|
608
|
+
for (const el of root.querySelectorAll("[data-ohw-section]")) {
|
|
609
|
+
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
610
|
+
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
611
|
+
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
612
|
+
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
613
|
+
continue;
|
|
614
|
+
seen.add(id);
|
|
615
|
+
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
616
|
+
sections.push({ id, label });
|
|
617
|
+
}
|
|
618
|
+
return sections;
|
|
619
|
+
}
|
|
620
|
+
function collectSectionsFromDom() {
|
|
621
|
+
if (typeof document === "undefined") return [];
|
|
622
|
+
return parseSectionsFromRoot(document);
|
|
623
|
+
}
|
|
624
|
+
function parseSectionsFromHtml(html) {
|
|
625
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
626
|
+
return parseSectionsFromRoot(doc);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/lib/section-instances.ts
|
|
630
|
+
var SECTION_ORDER_KEY = "__ohw_section_order";
|
|
631
|
+
var REMOVED_ATTR = "data-ohw-section-removed";
|
|
632
|
+
function isRemovedSection(el) {
|
|
633
|
+
return el.hasAttribute(REMOVED_ATTR);
|
|
634
|
+
}
|
|
635
|
+
function movableUnit(el) {
|
|
636
|
+
return el.closest("[data-ohw-section-container]") ?? el;
|
|
637
|
+
}
|
|
638
|
+
function sectionTypeOf(el) {
|
|
639
|
+
return el.getAttribute("data-ohw-section") ?? el.querySelector("[data-ohw-section]")?.getAttribute("data-ohw-section") ?? "";
|
|
640
|
+
}
|
|
641
|
+
function sectionElementOf(el) {
|
|
642
|
+
return el.hasAttribute("data-ohw-section") ? el : el.querySelector("[data-ohw-section]") ?? el;
|
|
643
|
+
}
|
|
644
|
+
function collectTopLevelUnits(predicate) {
|
|
645
|
+
const seen = /* @__PURE__ */ new Set();
|
|
646
|
+
const result = [];
|
|
647
|
+
document.querySelectorAll("[data-ohw-section]").forEach((el) => {
|
|
648
|
+
if (!predicate(el)) return;
|
|
649
|
+
const unit = movableUnit(el);
|
|
650
|
+
if (unit.parentElement?.closest("[data-ohw-section],[data-ohw-section-container]")) return;
|
|
651
|
+
if (seen.has(unit)) return;
|
|
652
|
+
seen.add(unit);
|
|
653
|
+
result.push(unit);
|
|
654
|
+
});
|
|
655
|
+
return result;
|
|
656
|
+
}
|
|
657
|
+
function topLevelSections() {
|
|
658
|
+
return collectTopLevelUnits((el) => !isChromeSection(el) && !isRemovedSection(movableUnit(el)));
|
|
659
|
+
}
|
|
660
|
+
function instanceIdOf(el) {
|
|
661
|
+
return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
|
|
662
|
+
}
|
|
663
|
+
function findByInstanceId(instanceId) {
|
|
664
|
+
const escapedId = CSS.escape(instanceId);
|
|
665
|
+
const direct = document.querySelector(`[data-ohw-instance="${escapedId}"]`);
|
|
666
|
+
if (direct) return movableUnit(direct);
|
|
667
|
+
const bare = document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
|
|
668
|
+
return bare ? movableUnit(bare) : null;
|
|
669
|
+
}
|
|
670
|
+
function planSectionMove(instanceId, targetIndex, currentPath) {
|
|
671
|
+
const sections = topLevelSections();
|
|
672
|
+
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
673
|
+
if (index === -1) return null;
|
|
674
|
+
const dragged = sections[index];
|
|
675
|
+
const others = sections.filter((_, i) => i !== index);
|
|
676
|
+
const clamped = Math.max(0, Math.min(targetIndex, others.length));
|
|
677
|
+
const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
|
|
678
|
+
return reordered.map((el, order) => ({
|
|
679
|
+
instanceId: instanceIdOf(el),
|
|
680
|
+
type: sectionTypeOf(el),
|
|
681
|
+
order,
|
|
682
|
+
pagePath: currentPath
|
|
683
|
+
}));
|
|
684
|
+
}
|
|
685
|
+
function moveSectionInstance(instanceId, direction, currentPath) {
|
|
686
|
+
const sections = topLevelSections();
|
|
687
|
+
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
688
|
+
if (index === -1) return null;
|
|
689
|
+
const siblingIndex = direction === "up" ? index - 1 : index + 1;
|
|
690
|
+
if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
|
|
691
|
+
const entries = planSectionMove(instanceId, siblingIndex, currentPath);
|
|
692
|
+
if (!entries) return null;
|
|
693
|
+
applyPersistedOrder(entries);
|
|
694
|
+
return entries;
|
|
695
|
+
}
|
|
696
|
+
function syncRemovedFlags(entries) {
|
|
697
|
+
const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
|
|
698
|
+
document.querySelectorAll(`[${REMOVED_ATTR}]`).forEach((el) => {
|
|
699
|
+
if (!removedIds.has(instanceIdOf(el))) {
|
|
700
|
+
el.style.removeProperty("display");
|
|
701
|
+
el.removeAttribute(REMOVED_ATTR);
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
for (const id of removedIds) {
|
|
705
|
+
const el = findByInstanceId(id);
|
|
706
|
+
if (el) {
|
|
707
|
+
el.style.display = "none";
|
|
708
|
+
el.setAttribute(REMOVED_ATTR, "");
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function applyPersistedOrder(entries) {
|
|
713
|
+
syncRemovedFlags(entries);
|
|
714
|
+
if (entries.length === 0) return;
|
|
715
|
+
const sections = topLevelSections();
|
|
716
|
+
if (sections.length === 0) return;
|
|
717
|
+
const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
|
|
718
|
+
const ordered = [...sections].sort((a, b) => {
|
|
719
|
+
const aOrder = orderIndex.get(instanceIdOf(a));
|
|
720
|
+
const bOrder = orderIndex.get(instanceIdOf(b));
|
|
721
|
+
if (aOrder === void 0 && bOrder === void 0) return 0;
|
|
722
|
+
if (aOrder === void 0) return 1;
|
|
723
|
+
if (bOrder === void 0) return -1;
|
|
724
|
+
return aOrder - bOrder;
|
|
725
|
+
});
|
|
726
|
+
let prev = null;
|
|
727
|
+
for (const el of ordered) {
|
|
728
|
+
if (prev) prev.after(el);
|
|
729
|
+
prev = el;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
|
|
733
|
+
if (!findByInstanceId(instanceId)) return null;
|
|
734
|
+
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
735
|
+
const allSections = collectTopLevelUnits((el) => !isChromeSection(el));
|
|
736
|
+
allSections.forEach((el, order) => {
|
|
737
|
+
const id = instanceIdOf(el);
|
|
738
|
+
if (!byId.has(id)) {
|
|
739
|
+
byId.set(id, { instanceId: id, type: sectionTypeOf(el), order, pagePath: currentPath });
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
const target = byId.get(instanceId);
|
|
743
|
+
if (!target) return null;
|
|
744
|
+
byId.set(instanceId, { ...target, removed });
|
|
745
|
+
const entries = Array.from(byId.values());
|
|
746
|
+
applyPersistedOrder(entries);
|
|
747
|
+
return entries;
|
|
748
|
+
}
|
|
749
|
+
function deleteSectionInstance(instanceId, currentPath, existingEntries) {
|
|
750
|
+
return setSectionRemoved(instanceId, currentPath, existingEntries, true);
|
|
751
|
+
}
|
|
752
|
+
function restoreSectionInstance(instanceId, currentPath, existingEntries) {
|
|
753
|
+
return setSectionRemoved(instanceId, currentPath, existingEntries, false);
|
|
754
|
+
}
|
|
755
|
+
function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
|
|
756
|
+
const original = findByInstanceId(instanceId);
|
|
757
|
+
if (!original) return null;
|
|
758
|
+
const clone = original.cloneNode(true);
|
|
759
|
+
clone.setAttribute("data-ohw-instance", newId);
|
|
760
|
+
const keyRekeys = rekeySectionSubtree(clone, newId);
|
|
761
|
+
original.insertAdjacentElement("afterend", clone);
|
|
762
|
+
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
763
|
+
const entries = topLevelSections().map((el, order) => {
|
|
764
|
+
const id = instanceIdOf(el);
|
|
765
|
+
return {
|
|
766
|
+
instanceId: id,
|
|
767
|
+
type: sectionTypeOf(el),
|
|
768
|
+
order,
|
|
769
|
+
pagePath: currentPath,
|
|
770
|
+
...byId.get(id)?.removed ? { removed: true } : {}
|
|
771
|
+
};
|
|
772
|
+
});
|
|
773
|
+
applyPersistedOrder(entries);
|
|
774
|
+
return { entries, keyRekeys };
|
|
775
|
+
}
|
|
776
|
+
function newInstanceId() {
|
|
777
|
+
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
778
|
+
}
|
|
779
|
+
function getPageSectionOrderEntries(raw, currentPath) {
|
|
780
|
+
if (!raw) return [];
|
|
781
|
+
try {
|
|
782
|
+
const entries = JSON.parse(raw);
|
|
783
|
+
return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
|
|
784
|
+
} catch {
|
|
785
|
+
return [];
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
function mergePageSectionOrder(raw, currentPath, pageEntries) {
|
|
789
|
+
let all = [];
|
|
790
|
+
if (raw) {
|
|
791
|
+
try {
|
|
792
|
+
const parsed = JSON.parse(raw);
|
|
793
|
+
if (Array.isArray(parsed)) all = parsed;
|
|
794
|
+
} catch {
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
const otherPages = all.filter((e) => e && e.pagePath && e.pagePath !== currentPath);
|
|
798
|
+
const pageIds = new Set(pageEntries.map((e) => e.instanceId));
|
|
799
|
+
const removedHere = all.filter(
|
|
800
|
+
(e) => e && (!e.pagePath || e.pagePath === currentPath) && e.removed && !pageIds.has(e.instanceId)
|
|
801
|
+
);
|
|
802
|
+
return [...otherPages, ...removedHere, ...pageEntries];
|
|
803
|
+
}
|
|
804
|
+
function rekeySectionSubtree(root, instanceId) {
|
|
805
|
+
const suffix = `::${instanceId}`;
|
|
806
|
+
const pairs = [];
|
|
807
|
+
const rekey = (el, attr) => {
|
|
808
|
+
const current = el.getAttribute(attr);
|
|
809
|
+
if (!current) return;
|
|
810
|
+
const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
|
|
811
|
+
const next = `${base}${suffix}`;
|
|
812
|
+
el.setAttribute(attr, next);
|
|
813
|
+
pairs.push({ from: current, to: next });
|
|
814
|
+
};
|
|
815
|
+
if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
|
|
816
|
+
if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
|
|
817
|
+
root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
|
|
818
|
+
root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
|
|
819
|
+
return pairs;
|
|
820
|
+
}
|
|
821
|
+
function initSectionInstancesFromContent(content, currentPath) {
|
|
822
|
+
document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
|
|
823
|
+
el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
|
|
824
|
+
});
|
|
825
|
+
document.querySelectorAll("[data-ohw-section-container]:not([data-ohw-instance])").forEach((el) => {
|
|
826
|
+
const type = sectionTypeOf(el);
|
|
827
|
+
if (type) el.setAttribute("data-ohw-instance", type);
|
|
828
|
+
});
|
|
829
|
+
const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
|
|
830
|
+
for (const entry of entries) {
|
|
831
|
+
if (entry.instanceId === entry.type) continue;
|
|
832
|
+
if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
|
|
833
|
+
const original = document.querySelector(
|
|
834
|
+
`[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
|
|
835
|
+
);
|
|
836
|
+
if (!original) continue;
|
|
837
|
+
const clone = original.cloneNode(true);
|
|
838
|
+
clone.setAttribute("data-ohw-instance", entry.instanceId);
|
|
839
|
+
rekeySectionSubtree(clone, entry.instanceId);
|
|
840
|
+
original.insertAdjacentElement("afterend", clone);
|
|
841
|
+
}
|
|
842
|
+
applyPersistedOrder(entries);
|
|
843
|
+
}
|
|
844
|
+
|
|
471
845
|
// src/ui/ai-tree/AiTreeRenderer.tsx
|
|
472
846
|
import React from "react";
|
|
473
847
|
import { ArrowLeft, ArrowRight, ChevronDown, icons as lucideIcons } from "lucide-react";
|
|
848
|
+
|
|
849
|
+
// src/lib/placeholder-imagery.ts
|
|
850
|
+
var U = (id) => `https://images.unsplash.com/photo-${id}?auto=format&fit=crop&w=1600&q=80`;
|
|
851
|
+
var GENERIC = [
|
|
852
|
+
U("1441986300917-64674bd600d8"),
|
|
853
|
+
U("1486406146926-c627a92ad1ab"),
|
|
854
|
+
U("1497032628192-86f99bcd76bc"),
|
|
855
|
+
U("1521737604893-d14cc237f11d"),
|
|
856
|
+
U("1522071820081-009f0129c71c"),
|
|
857
|
+
U("1519389950473-47ba0277781c"),
|
|
858
|
+
U("1460925895917-afdab827c52f"),
|
|
859
|
+
U("1504384308090-c894fdcc538d")
|
|
860
|
+
];
|
|
861
|
+
var PEOPLE = [
|
|
862
|
+
U("1500648767791-00dcc994a43e"),
|
|
863
|
+
U("1494790108377-be9c29b29330"),
|
|
864
|
+
U("1507003211169-0a1dd7228f2d"),
|
|
865
|
+
U("1438761681033-6461ffad8d80"),
|
|
866
|
+
U("1544005313-94ddf0286df2"),
|
|
867
|
+
U("1472099645785-5658abf4ff4e"),
|
|
868
|
+
U("1519085360753-af0119f7cbe7"),
|
|
869
|
+
U("1534528741775-53994a69daeb")
|
|
870
|
+
];
|
|
871
|
+
var THEMED = [
|
|
872
|
+
{
|
|
873
|
+
keywords: ["portrait", "headshot", "person", "people", "team", "staff", "avatar", "founder", "face"],
|
|
874
|
+
pool: PEOPLE
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
keywords: ["pet", "dog", "cat", "puppy", "kitten", "vet", "animal"],
|
|
878
|
+
pool: [
|
|
879
|
+
U("1548199973-03cce0bbc87b"),
|
|
880
|
+
U("1450778869180-41d0601e046e"),
|
|
881
|
+
U("1583511655857-d19b40a7a54e"),
|
|
882
|
+
U("1587300003388-59208cc962cb"),
|
|
883
|
+
U("1517849845537-4d257902454a"),
|
|
884
|
+
U("1601758228041-f3b2795255f1")
|
|
885
|
+
]
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
keywords: [
|
|
889
|
+
"baker",
|
|
890
|
+
"bakery",
|
|
891
|
+
"cafe",
|
|
892
|
+
"coffee",
|
|
893
|
+
"latte",
|
|
894
|
+
"restaurant",
|
|
895
|
+
"pastr",
|
|
896
|
+
"bread",
|
|
897
|
+
"cake",
|
|
898
|
+
"cater",
|
|
899
|
+
"chef",
|
|
900
|
+
"kitchen",
|
|
901
|
+
"food",
|
|
902
|
+
"pizza",
|
|
903
|
+
"dessert",
|
|
904
|
+
"brunch",
|
|
905
|
+
"bistro",
|
|
906
|
+
"deli",
|
|
907
|
+
"dish",
|
|
908
|
+
"menu"
|
|
909
|
+
],
|
|
910
|
+
pool: [
|
|
911
|
+
U("1509440159596-0249088772ff"),
|
|
912
|
+
U("1555507036-ab1f4038808a"),
|
|
913
|
+
U("1517433670267-08bbd4be890f"),
|
|
914
|
+
U("1486427944299-d1955d23e34d"),
|
|
915
|
+
U("1504754524776-8f4f37790ca0"),
|
|
916
|
+
U("1495474472287-4d71bcdd2085"),
|
|
917
|
+
U("1521017432531-fbd92d768814"),
|
|
918
|
+
U("1556909114-f6e7ad7d3136")
|
|
919
|
+
]
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
keywords: [
|
|
923
|
+
"shop",
|
|
924
|
+
"store",
|
|
925
|
+
"boutique",
|
|
926
|
+
"retail",
|
|
927
|
+
"clothing",
|
|
928
|
+
"fashion",
|
|
929
|
+
"jewel",
|
|
930
|
+
"gift",
|
|
931
|
+
"florist",
|
|
932
|
+
"market",
|
|
933
|
+
"grocer",
|
|
934
|
+
"product",
|
|
935
|
+
"storefront"
|
|
936
|
+
],
|
|
937
|
+
pool: [
|
|
938
|
+
U("1441984904996-e0b6ba687e04"),
|
|
939
|
+
U("1472851294608-062f824d29cc"),
|
|
940
|
+
U("1523381210434-271e8be1f52b"),
|
|
941
|
+
U("1534452203293-494d7ddbf7e0"),
|
|
942
|
+
U("1445205170230-053b83016050"),
|
|
943
|
+
U("1560243563-062bfc001d68")
|
|
944
|
+
]
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
keywords: [
|
|
948
|
+
"yoga",
|
|
949
|
+
"pilates",
|
|
950
|
+
"fitness",
|
|
951
|
+
"gym",
|
|
952
|
+
"workout",
|
|
953
|
+
"trainer",
|
|
954
|
+
"wellness",
|
|
955
|
+
"meditat",
|
|
956
|
+
"massage",
|
|
957
|
+
"therap",
|
|
958
|
+
"physio",
|
|
959
|
+
"chiro",
|
|
960
|
+
"nutrition",
|
|
961
|
+
"spa",
|
|
962
|
+
"studio"
|
|
963
|
+
],
|
|
964
|
+
pool: [
|
|
965
|
+
U("1544367567-0f2fcb009e0b"),
|
|
966
|
+
U("1506126613408-eca07ce68773"),
|
|
967
|
+
U("1545205597-3d9d02c29597"),
|
|
968
|
+
U("1552196563-55cd4e45efb3"),
|
|
969
|
+
U("1518611012118-696072aa579a"),
|
|
970
|
+
U("1571019613454-1cb2f99b2d8b"),
|
|
971
|
+
U("1540555700478-4be289fbecef"),
|
|
972
|
+
U("1519824145371-296894a0daa9")
|
|
973
|
+
]
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
keywords: [
|
|
977
|
+
"salon",
|
|
978
|
+
"hairdress",
|
|
979
|
+
"haircut",
|
|
980
|
+
"barber",
|
|
981
|
+
"manicure",
|
|
982
|
+
"pedicure",
|
|
983
|
+
"nails",
|
|
984
|
+
"beauty",
|
|
985
|
+
"makeup",
|
|
986
|
+
"cosmetic",
|
|
987
|
+
"eyelash",
|
|
988
|
+
"eyebrow",
|
|
989
|
+
"skincare",
|
|
990
|
+
"esthetic",
|
|
991
|
+
"waxing",
|
|
992
|
+
"hair"
|
|
993
|
+
],
|
|
994
|
+
pool: [
|
|
995
|
+
U("1560066984-138dadb4c035"),
|
|
996
|
+
U("1522337660859-02fbefca4702"),
|
|
997
|
+
U("1562322140-8baeececf3df"),
|
|
998
|
+
U("1521590832167-7bcbfaa6381f"),
|
|
999
|
+
U("1487412947147-5cebf100ffc2"),
|
|
1000
|
+
U("1526045478516-99145907023c")
|
|
1001
|
+
]
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
keywords: [
|
|
1005
|
+
"cleaning",
|
|
1006
|
+
"plumb",
|
|
1007
|
+
"electric",
|
|
1008
|
+
"landscap",
|
|
1009
|
+
"contractor",
|
|
1010
|
+
"handyman",
|
|
1011
|
+
"renov",
|
|
1012
|
+
"hvac",
|
|
1013
|
+
"roofing",
|
|
1014
|
+
"painting",
|
|
1015
|
+
"carpentry",
|
|
1016
|
+
"flooring",
|
|
1017
|
+
"movers",
|
|
1018
|
+
"construction",
|
|
1019
|
+
"tools"
|
|
1020
|
+
],
|
|
1021
|
+
pool: [
|
|
1022
|
+
U("1581578731548-c64695cc6952"),
|
|
1023
|
+
U("1504307651254-35680f356dfd"),
|
|
1024
|
+
U("1581092160562-40aa08e78837"),
|
|
1025
|
+
U("1621905251189-08b45d6a269e"),
|
|
1026
|
+
U("1558618666-fcd25c85cd64"),
|
|
1027
|
+
U("1585128792020-803d29415281")
|
|
1028
|
+
]
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
keywords: [
|
|
1032
|
+
"legal",
|
|
1033
|
+
"attorney",
|
|
1034
|
+
"lawyer",
|
|
1035
|
+
"account",
|
|
1036
|
+
"bookkeep",
|
|
1037
|
+
"consult",
|
|
1038
|
+
"coaching",
|
|
1039
|
+
"financ",
|
|
1040
|
+
"insurance",
|
|
1041
|
+
"realtor",
|
|
1042
|
+
"estate",
|
|
1043
|
+
"marketing",
|
|
1044
|
+
"agency",
|
|
1045
|
+
"office",
|
|
1046
|
+
"business",
|
|
1047
|
+
"desk"
|
|
1048
|
+
],
|
|
1049
|
+
pool: [
|
|
1050
|
+
U("1497366216548-37526070297c"),
|
|
1051
|
+
U("1497366811353-6870744d04b2"),
|
|
1052
|
+
U("1454165804606-c3d57bc86b40"),
|
|
1053
|
+
U("1521791136064-7986c2920216"),
|
|
1054
|
+
U("1556761175-b413da4baf72"),
|
|
1055
|
+
U("1542744173-8e7e53415bb0")
|
|
1056
|
+
]
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
keywords: [
|
|
1060
|
+
"wedding",
|
|
1061
|
+
"event",
|
|
1062
|
+
"party",
|
|
1063
|
+
"celebrat",
|
|
1064
|
+
"venue",
|
|
1065
|
+
"community",
|
|
1066
|
+
"nonprofit",
|
|
1067
|
+
"charity",
|
|
1068
|
+
"workshop",
|
|
1069
|
+
"photograph",
|
|
1070
|
+
"concert"
|
|
1071
|
+
],
|
|
1072
|
+
pool: [
|
|
1073
|
+
U("1511578314322-379afb476865"),
|
|
1074
|
+
U("1501281668745-f7f57925c3b4"),
|
|
1075
|
+
U("1523580494863-6f3031224c94"),
|
|
1076
|
+
U("1540575467063-178a50c2df87"),
|
|
1077
|
+
U("1505236858219-8359eb29e329"),
|
|
1078
|
+
U("1528605248644-14dd04022da1")
|
|
1079
|
+
]
|
|
1080
|
+
}
|
|
1081
|
+
];
|
|
1082
|
+
function poolForSubject(subject) {
|
|
1083
|
+
for (const theme of THEMED) {
|
|
1084
|
+
if (theme.keywords.some((k) => subject.includes(k))) {
|
|
1085
|
+
return theme.pool;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
return GENERIC;
|
|
1089
|
+
}
|
|
1090
|
+
function mixedHash(text) {
|
|
1091
|
+
let hash = 2166136261;
|
|
1092
|
+
for (let i = 0; i < text.length; i++) {
|
|
1093
|
+
hash ^= text.charCodeAt(i);
|
|
1094
|
+
hash = Math.imul(hash, 16777619);
|
|
1095
|
+
}
|
|
1096
|
+
return hash >>> 16 & 65535;
|
|
1097
|
+
}
|
|
1098
|
+
function resolvePlaceholderRef(ref) {
|
|
1099
|
+
const match = /^placeholder:([a-z0-9-]+)$/.exec(ref);
|
|
1100
|
+
if (!match) return null;
|
|
1101
|
+
const subject = match[1];
|
|
1102
|
+
const pool = poolForSubject(subject.replace(/-\d+$/, ""));
|
|
1103
|
+
return pool[mixedHash(ref) % pool.length];
|
|
1104
|
+
}
|
|
1105
|
+
function collectPlaceholderRefs(tree) {
|
|
1106
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1107
|
+
for (const match of JSON.stringify(tree ?? null).matchAll(/"(placeholder:[a-z0-9-]+)"/gu)) {
|
|
1108
|
+
seen.add(match[1]);
|
|
1109
|
+
}
|
|
1110
|
+
return [...seen];
|
|
1111
|
+
}
|
|
1112
|
+
function buildPlaceholderMap(tree) {
|
|
1113
|
+
const map = {};
|
|
1114
|
+
const cursor = /* @__PURE__ */ new Map();
|
|
1115
|
+
for (const ref of collectPlaceholderRefs(tree)) {
|
|
1116
|
+
const subject = ref.slice("placeholder:".length).replace(/-\d+$/, "");
|
|
1117
|
+
const pool = poolForSubject(subject);
|
|
1118
|
+
const start = cursor.get(pool) ?? mixedHash(ref) % pool.length;
|
|
1119
|
+
map[ref] = pool[start % pool.length];
|
|
1120
|
+
cursor.set(pool, start + 1);
|
|
1121
|
+
}
|
|
1122
|
+
return map;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// src/ui/ai-tree/AiTreeRenderer.tsx
|
|
474
1126
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
475
1127
|
function lucideByName(name) {
|
|
476
1128
|
const pascal = name.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
@@ -484,6 +1136,7 @@ var typeStyle = (spec, font) => ({
|
|
|
484
1136
|
fontWeight: spec.weight
|
|
485
1137
|
});
|
|
486
1138
|
var str = (value) => typeof value === "string" ? value : "";
|
|
1139
|
+
var cardRadius = (slots) => slots.cornerStyle === "sharp" ? 0 : AI_TREE_TOKENS.radiusCard;
|
|
487
1140
|
var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.trim()).filter(Boolean);
|
|
488
1141
|
var CHECK_MASK = `data:image/svg+xml,${encodeURIComponent(
|
|
489
1142
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>'
|
|
@@ -510,6 +1163,25 @@ var FEATURE_LINE_CSS = [
|
|
|
510
1163
|
`background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
|
|
511
1164
|
`mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
|
|
512
1165
|
].join("");
|
|
1166
|
+
function buttonShellStyle(ctx, fullWidth) {
|
|
1167
|
+
const bs = ctx.buttonStyle;
|
|
1168
|
+
if (bs) {
|
|
1169
|
+
return {
|
|
1170
|
+
borderRadius: bs.radius,
|
|
1171
|
+
...bs.padding ? { padding: bs.padding } : {},
|
|
1172
|
+
...bs.fontFamily ? { fontFamily: bs.fontFamily } : { fontFamily: ctx.brand.fonts.body },
|
|
1173
|
+
...bs.fontSize ? { fontSize: bs.fontSize } : {},
|
|
1174
|
+
...bs.fontWeight ? { fontWeight: bs.fontWeight } : {},
|
|
1175
|
+
...bs.letterSpacing && bs.letterSpacing !== "normal" ? { letterSpacing: bs.letterSpacing } : {},
|
|
1176
|
+
...bs.textTransform && bs.textTransform !== "none" ? { textTransform: bs.textTransform } : {}
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
return {
|
|
1180
|
+
borderRadius: AI_TREE_TOKENS.radiusButton,
|
|
1181
|
+
padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
|
|
1182
|
+
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
513
1185
|
function hexLuminance(color) {
|
|
514
1186
|
const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
515
1187
|
if (!m) return null;
|
|
@@ -526,6 +1198,12 @@ function hexContrast(a, b) {
|
|
|
526
1198
|
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
527
1199
|
return (hi + 0.05) / (lo + 0.05);
|
|
528
1200
|
}
|
|
1201
|
+
function primaryButtonLabel(brand) {
|
|
1202
|
+
const darkC = hexContrast(brand.palette.primary, brand.palette.dark);
|
|
1203
|
+
const lightC = hexContrast(brand.palette.primary, AI_TREE_TOKENS.textPrimaryForeground);
|
|
1204
|
+
if (darkC === null || lightC === null) return AI_TREE_TOKENS.textPrimaryForeground;
|
|
1205
|
+
return darkC > lightC ? brand.palette.dark : AI_TREE_TOKENS.textPrimaryForeground;
|
|
1206
|
+
}
|
|
529
1207
|
function accentBandContext(brand) {
|
|
530
1208
|
const p = brand.palette;
|
|
531
1209
|
const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
|
|
@@ -543,6 +1221,22 @@ function accentBandContext(brand) {
|
|
|
543
1221
|
function textAttrs(ctx, path) {
|
|
544
1222
|
return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
|
|
545
1223
|
}
|
|
1224
|
+
var AI_RESPONSIVE_CSS = [
|
|
1225
|
+
"@media (max-width: 960px) {",
|
|
1226
|
+
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
|
|
1227
|
+
' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
|
|
1228
|
+
"}",
|
|
1229
|
+
"@media (max-width: 640px) {",
|
|
1230
|
+
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
|
|
1231
|
+
" [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
|
|
1232
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
1233
|
+
" [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
|
|
1234
|
+
" [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
|
|
1235
|
+
" [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
|
|
1236
|
+
" [data-ai-responsive] { overflow-x: hidden; }",
|
|
1237
|
+
" [data-ai-responsive] img { max-width: 100%; }",
|
|
1238
|
+
"}"
|
|
1239
|
+
].join("\n");
|
|
546
1240
|
var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
547
1241
|
function MediaBox({
|
|
548
1242
|
refValue,
|
|
@@ -555,13 +1249,17 @@ function MediaBox({
|
|
|
555
1249
|
const url = refValue ? ctx.resolveMedia(refValue) : null;
|
|
556
1250
|
const isIcon = /^(lucide|simple):/.test(refValue);
|
|
557
1251
|
const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
|
|
558
|
-
const editAttrs = ctx.keyFor && editPath
|
|
1252
|
+
const editAttrs = ctx.keyFor && editPath ? {
|
|
1253
|
+
"data-ohw-key": ctx.keyFor(editPath),
|
|
1254
|
+
"data-ohw-editable": isIcon ? "icon" : "image"
|
|
1255
|
+
} : {};
|
|
559
1256
|
if (isIcon) {
|
|
560
1257
|
const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
|
|
561
1258
|
return /* @__PURE__ */ jsx(
|
|
562
1259
|
"span",
|
|
563
1260
|
{
|
|
564
1261
|
"data-ai-icon": refValue,
|
|
1262
|
+
...editAttrs,
|
|
565
1263
|
style: {
|
|
566
1264
|
display: "inline-flex",
|
|
567
1265
|
width: 48,
|
|
@@ -625,12 +1323,10 @@ function ButtonEl({
|
|
|
625
1323
|
width: fullWidth ? "100%" : void 0,
|
|
626
1324
|
alignItems: "center",
|
|
627
1325
|
justifyContent: "center",
|
|
628
|
-
padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
|
|
629
|
-
borderRadius: AI_TREE_TOKENS.radiusButton,
|
|
630
1326
|
textDecoration: "none",
|
|
631
1327
|
cursor: "pointer",
|
|
632
|
-
...
|
|
633
|
-
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ??
|
|
1328
|
+
...buttonShellStyle(ctx, fullWidth),
|
|
1329
|
+
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
|
|
634
1330
|
},
|
|
635
1331
|
children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
|
|
636
1332
|
}
|
|
@@ -656,7 +1352,7 @@ function TextBlock({ slots, ctx, path }) {
|
|
|
656
1352
|
}
|
|
657
1353
|
function SectionHeaderBlock({ node, ctx, path }) {
|
|
658
1354
|
const slots = node.slots ?? {};
|
|
659
|
-
const align = slots.alignment === "center" ? "center" : "left";
|
|
1355
|
+
const align = node.align ?? (slots.alignment === "center" ? "center" : "left");
|
|
660
1356
|
const children = node.children ?? [];
|
|
661
1357
|
const buttonRowIdx = children.findIndex((c) => c.type === "button-row");
|
|
662
1358
|
const buttonRow = buttonRowIdx >= 0 ? children[buttonRowIdx] : void 0;
|
|
@@ -700,7 +1396,7 @@ function SectionHeaderBlock({ node, ctx, path }) {
|
|
|
700
1396
|
display: "flex",
|
|
701
1397
|
gap: AI_TREE_TOKENS.spacing6,
|
|
702
1398
|
marginTop: AI_TREE_TOKENS.spacing8,
|
|
703
|
-
justifyContent: align === "center" ? "center" : "flex-start"
|
|
1399
|
+
justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start"
|
|
704
1400
|
},
|
|
705
1401
|
children: (buttonRow.children ?? []).map((button, i) => /* @__PURE__ */ jsx(
|
|
706
1402
|
ButtonEl,
|
|
@@ -806,10 +1502,11 @@ function PricingCard({ node, ctx, path }) {
|
|
|
806
1502
|
return /* @__PURE__ */ jsxs(
|
|
807
1503
|
"div",
|
|
808
1504
|
{
|
|
1505
|
+
"data-ohw-card": "",
|
|
809
1506
|
style: {
|
|
810
1507
|
background: hasBg ? ctx.brand.palette.light : "transparent",
|
|
811
1508
|
border: `1px solid ${dark}`,
|
|
812
|
-
borderRadius:
|
|
1509
|
+
borderRadius: cardRadius(slots),
|
|
813
1510
|
padding: AI_TREE_TOKENS.paddingBlock,
|
|
814
1511
|
display: "flex",
|
|
815
1512
|
flexDirection: "column",
|
|
@@ -914,10 +1611,11 @@ function TestimonialCard({ node, ctx, path }) {
|
|
|
914
1611
|
return /* @__PURE__ */ jsx(
|
|
915
1612
|
"div",
|
|
916
1613
|
{
|
|
1614
|
+
"data-ohw-card": "",
|
|
917
1615
|
"data-ai-avatar-pos": avatarPos ?? void 0,
|
|
918
1616
|
style: {
|
|
919
1617
|
background: hasBg ? ctx.cardSurface : "transparent",
|
|
920
|
-
borderRadius: hasBg ?
|
|
1618
|
+
borderRadius: hasBg ? cardRadius(slots) : 0,
|
|
921
1619
|
overflow: "hidden",
|
|
922
1620
|
outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
|
|
923
1621
|
minWidth: 0
|
|
@@ -952,10 +1650,11 @@ function TeamCard({ node, ctx, path }) {
|
|
|
952
1650
|
return /* @__PURE__ */ jsxs(
|
|
953
1651
|
"div",
|
|
954
1652
|
{
|
|
1653
|
+
"data-ohw-card": "",
|
|
955
1654
|
"data-ai-avatar-pos": avatarPos ?? void 0,
|
|
956
1655
|
style: {
|
|
957
1656
|
background: hasBg ? ctx.cardSurface : "transparent",
|
|
958
|
-
borderRadius: hasBg ?
|
|
1657
|
+
borderRadius: hasBg ? cardRadius(slots) : 0,
|
|
959
1658
|
overflow: "hidden",
|
|
960
1659
|
outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
|
|
961
1660
|
minWidth: 0,
|
|
@@ -1033,7 +1732,7 @@ function CardBlock({ node, ctx, path }) {
|
|
|
1033
1732
|
editPath: `${path}.media`
|
|
1034
1733
|
}
|
|
1035
1734
|
) : null;
|
|
1036
|
-
const centered = slots.alignment === "center";
|
|
1735
|
+
const centered = (node.align ?? slots.alignment) === "center";
|
|
1037
1736
|
const content = /* @__PURE__ */ jsxs(
|
|
1038
1737
|
"div",
|
|
1039
1738
|
{
|
|
@@ -1126,9 +1825,10 @@ function CardBlock({ node, ctx, path }) {
|
|
|
1126
1825
|
return /* @__PURE__ */ jsxs(
|
|
1127
1826
|
"div",
|
|
1128
1827
|
{
|
|
1828
|
+
"data-ohw-card": "",
|
|
1129
1829
|
style: {
|
|
1130
1830
|
background: hasBg ? ctx.cardSurface : "transparent",
|
|
1131
|
-
borderRadius: hasBg ?
|
|
1831
|
+
borderRadius: hasBg ? cardRadius(slots) : 0,
|
|
1132
1832
|
overflow: "hidden",
|
|
1133
1833
|
outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
|
|
1134
1834
|
display: horizontal ? "flex" : "block",
|
|
@@ -1156,7 +1856,7 @@ function CardBlock({ node, ctx, path }) {
|
|
|
1156
1856
|
) : /* @__PURE__ */ jsx(
|
|
1157
1857
|
"div",
|
|
1158
1858
|
{
|
|
1159
|
-
style: /^(lucide|simple):/.test(mediaRef) ? { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` } : hasBg ? void 0 : { borderRadius:
|
|
1859
|
+
style: /^(lucide|simple):/.test(mediaRef) ? { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` } : hasBg ? void 0 : { borderRadius: cardRadius(slots), overflow: "hidden" },
|
|
1160
1860
|
children: media
|
|
1161
1861
|
}
|
|
1162
1862
|
)),
|
|
@@ -1447,7 +2147,7 @@ function CollectionBlock({ node, ctx, path }) {
|
|
|
1447
2147
|
return /* @__PURE__ */ jsx(
|
|
1448
2148
|
"div",
|
|
1449
2149
|
{
|
|
1450
|
-
"data-ai-grid":
|
|
2150
|
+
"data-ai-grid": String(itemsPerRow),
|
|
1451
2151
|
style: {
|
|
1452
2152
|
display: "grid",
|
|
1453
2153
|
gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
|
|
@@ -1567,6 +2267,32 @@ function renderNode(node, ctx, path) {
|
|
|
1567
2267
|
if (child) {
|
|
1568
2268
|
return renderNode(child, ctx, `${path}.c0`);
|
|
1569
2269
|
}
|
|
2270
|
+
if (str(slots.provider) === "map" && str(slots.query)) {
|
|
2271
|
+
const query = str(slots.query);
|
|
2272
|
+
const mapAttrs = ctx.keyFor ? {
|
|
2273
|
+
"data-ohw-key": ctx.keyFor(`${path}.query`),
|
|
2274
|
+
"data-ohw-editable": "map",
|
|
2275
|
+
"data-ohw-map-query": query
|
|
2276
|
+
} : {};
|
|
2277
|
+
return /* @__PURE__ */ jsx(
|
|
2278
|
+
"iframe",
|
|
2279
|
+
{
|
|
2280
|
+
...mapAttrs,
|
|
2281
|
+
"data-ai-embed": "map",
|
|
2282
|
+
title: str(slots.title) || "Map",
|
|
2283
|
+
src: `https://www.google.com/maps?q=${encodeURIComponent(query)}&output=embed`,
|
|
2284
|
+
loading: "lazy",
|
|
2285
|
+
referrerPolicy: "no-referrer-when-downgrade",
|
|
2286
|
+
style: {
|
|
2287
|
+
width: "100%",
|
|
2288
|
+
minHeight: 320,
|
|
2289
|
+
border: 0,
|
|
2290
|
+
borderRadius: AI_TREE_TOKENS.radiusCard,
|
|
2291
|
+
display: "block"
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
);
|
|
2295
|
+
}
|
|
1570
2296
|
return /* @__PURE__ */ jsx(
|
|
1571
2297
|
"div",
|
|
1572
2298
|
{
|
|
@@ -1670,15 +2396,12 @@ function renderNode(node, ctx, path) {
|
|
|
1670
2396
|
alignSelf: submitAlign,
|
|
1671
2397
|
border: "none",
|
|
1672
2398
|
cursor: "pointer",
|
|
1673
|
-
padding
|
|
1674
|
-
|
|
1675
|
-
// CTA); 8px only when the page has no template button to match.
|
|
1676
|
-
borderRadius: ctx.buttonRadius ?? 8,
|
|
2399
|
+
// Shape/padding/typography follow the host template's own buttons.
|
|
2400
|
+
...buttonShellStyle(ctx),
|
|
1677
2401
|
// Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
|
|
1678
2402
|
// reads correctly on custom palettes.
|
|
1679
2403
|
background: ctx.brand.palette.primary,
|
|
1680
|
-
color: ctx.buttonLabel ?? ctx.brand
|
|
1681
|
-
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
2404
|
+
color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
|
|
1682
2405
|
},
|
|
1683
2406
|
children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
|
|
1684
2407
|
},
|
|
@@ -1713,7 +2436,7 @@ function renderNode(node, ctx, path) {
|
|
|
1713
2436
|
function AiTreeRenderer({
|
|
1714
2437
|
tree,
|
|
1715
2438
|
brand,
|
|
1716
|
-
|
|
2439
|
+
buttonStyle,
|
|
1717
2440
|
resolveMedia,
|
|
1718
2441
|
editKeyPrefix
|
|
1719
2442
|
}) {
|
|
@@ -1723,13 +2446,18 @@ function AiTreeRenderer({
|
|
|
1723
2446
|
const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
|
|
1724
2447
|
const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
|
|
1725
2448
|
const blockBrand = band?.brand ?? resolvedBrand;
|
|
2449
|
+
const placeholderMap = buildPlaceholderMap(tree);
|
|
1726
2450
|
const ctx = {
|
|
1727
2451
|
brand: blockBrand,
|
|
1728
|
-
|
|
2452
|
+
// An owner/library ref resolves through the host resolver; a `placeholder:<subject>` ref the
|
|
2453
|
+
// host cannot resolve falls back to real stock photography (the per-section map first, then a
|
|
2454
|
+
// standalone resolve), so generated galleries, image rows, and overlay backgrounds arrive with
|
|
2455
|
+
// photos instead of grey boxes.
|
|
2456
|
+
resolveMedia: (ref) => resolveMedia?.(ref) ?? placeholderMap[ref] ?? resolvePlaceholderRef(ref),
|
|
1729
2457
|
cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
|
|
1730
2458
|
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
|
|
1731
2459
|
sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
|
|
1732
|
-
|
|
2460
|
+
buttonStyle,
|
|
1733
2461
|
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
1734
2462
|
};
|
|
1735
2463
|
const settings = tree.settings ?? {};
|
|
@@ -1752,11 +2480,25 @@ function AiTreeRenderer({
|
|
|
1752
2480
|
}
|
|
1753
2481
|
})();
|
|
1754
2482
|
const distributed = !isOverlay && settings.textDistribution;
|
|
2483
|
+
const rowAlignItems = (rowAlign) => {
|
|
2484
|
+
if (rowAlign === "top") return "start";
|
|
2485
|
+
if (rowAlign === "bottom") return "end";
|
|
2486
|
+
if (rowAlign === "center" || rowAlign === "stretch") return rowAlign;
|
|
2487
|
+
if (distributed === "space-between") return "stretch";
|
|
2488
|
+
return (distributed ?? settings.verticalPosition) === "top" ? "start" : "center";
|
|
2489
|
+
};
|
|
2490
|
+
const cellAlignStyle = (blockAlign) => blockAlign ? {
|
|
2491
|
+
display: "flex",
|
|
2492
|
+
flexDirection: "column",
|
|
2493
|
+
alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
|
|
2494
|
+
textAlign: blockAlign
|
|
2495
|
+
} : {};
|
|
1755
2496
|
return /* @__PURE__ */ jsxs(
|
|
1756
2497
|
"section",
|
|
1757
2498
|
{
|
|
1758
2499
|
"data-ai-section": tree.tag ?? "",
|
|
1759
2500
|
...bgAttrs,
|
|
2501
|
+
"data-ai-responsive": "",
|
|
1760
2502
|
style: {
|
|
1761
2503
|
position: "relative",
|
|
1762
2504
|
padding: `${pad}px 0`,
|
|
@@ -1767,12 +2509,13 @@ function AiTreeRenderer({
|
|
|
1767
2509
|
color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
|
|
1768
2510
|
},
|
|
1769
2511
|
children: [
|
|
1770
|
-
|
|
2512
|
+
/* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
|
|
1771
2513
|
/* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
|
|
2514
|
+
isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
|
|
1772
2515
|
/* @__PURE__ */ jsx(
|
|
1773
2516
|
"div",
|
|
1774
2517
|
{
|
|
1775
|
-
"data-ai-
|
|
2518
|
+
"data-ai-section-inner": "",
|
|
1776
2519
|
style: {
|
|
1777
2520
|
position: "relative",
|
|
1778
2521
|
maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
|
|
@@ -1783,12 +2526,12 @@ function AiTreeRenderer({
|
|
|
1783
2526
|
children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
|
|
1784
2527
|
"div",
|
|
1785
2528
|
{
|
|
1786
|
-
"data-ai-
|
|
2529
|
+
"data-ai-columns": "",
|
|
1787
2530
|
style: {
|
|
1788
2531
|
display: "grid",
|
|
1789
2532
|
gridTemplateColumns: "repeat(12, 1fr)",
|
|
1790
2533
|
gap: AI_TREE_TOKENS.spacing6,
|
|
1791
|
-
alignItems:
|
|
2534
|
+
alignItems: rowAlignItems(row.align),
|
|
1792
2535
|
marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
|
|
1793
2536
|
},
|
|
1794
2537
|
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
|
|
@@ -1798,6 +2541,8 @@ function AiTreeRenderer({
|
|
|
1798
2541
|
style: {
|
|
1799
2542
|
gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
|
|
1800
2543
|
minWidth: 0,
|
|
2544
|
+
// Horizontal placement of the block's content within its column.
|
|
2545
|
+
...cellAlignStyle(block.align),
|
|
1801
2546
|
// space-between: each column becomes a flex column whose content spreads over
|
|
1802
2547
|
// the full row height instead of clumping at the top.
|
|
1803
2548
|
...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
|
|
@@ -1820,9 +2565,13 @@ function AiTreeRenderer({
|
|
|
1820
2565
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1821
2566
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
1822
2567
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
1823
|
-
var
|
|
2568
|
+
var REMOVED_ATTR2 = "data-ohw-ai-removed";
|
|
1824
2569
|
var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
|
|
1825
2570
|
var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
2571
|
+
function isChromeSection2(el) {
|
|
2572
|
+
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) return true;
|
|
2573
|
+
return el.tagName === "HEADER" || el.tagName === "FOOTER";
|
|
2574
|
+
}
|
|
1826
2575
|
function readRootVar(name) {
|
|
1827
2576
|
if (typeof document === "undefined") return "";
|
|
1828
2577
|
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
@@ -1844,13 +2593,13 @@ function deriveBrandOverride() {
|
|
|
1844
2593
|
};
|
|
1845
2594
|
}
|
|
1846
2595
|
function deriveTemplateBrand() {
|
|
1847
|
-
const
|
|
1848
|
-
const
|
|
1849
|
-
const light = readRootVar("--color-light");
|
|
2596
|
+
const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
|
|
2597
|
+
const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
|
|
2598
|
+
const light = readRootVar("--brand-background") || readRootVar("--color-light");
|
|
1850
2599
|
if (!dark || !primary || !light) return null;
|
|
1851
|
-
const accent = readRootVar("--color-accent");
|
|
1852
|
-
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
1853
|
-
const body = readRootVar("--font-body");
|
|
2600
|
+
const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
|
|
2601
|
+
const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
|
|
2602
|
+
const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
|
|
1854
2603
|
return {
|
|
1855
2604
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1856
2605
|
fonts: {
|
|
@@ -1859,12 +2608,35 @@ function deriveTemplateBrand() {
|
|
|
1859
2608
|
}
|
|
1860
2609
|
};
|
|
1861
2610
|
}
|
|
1862
|
-
function
|
|
2611
|
+
function deriveTemplateButtonStyle() {
|
|
1863
2612
|
if (typeof document === "undefined") return null;
|
|
1864
|
-
const btn = document.
|
|
2613
|
+
const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
|
|
2614
|
+
(el) => !el.closest(`[${CONTAINER_ATTR}]`)
|
|
2615
|
+
);
|
|
1865
2616
|
if (!btn) return null;
|
|
1866
|
-
const
|
|
1867
|
-
|
|
2617
|
+
const cs = getComputedStyle(btn);
|
|
2618
|
+
const corners = [
|
|
2619
|
+
cs.borderTopLeftRadius,
|
|
2620
|
+
cs.borderTopRightRadius,
|
|
2621
|
+
cs.borderBottomRightRadius,
|
|
2622
|
+
cs.borderBottomLeftRadius
|
|
2623
|
+
].map((v) => v || "0px");
|
|
2624
|
+
const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
|
|
2625
|
+
const px = (v) => parseFloat(v) || 0;
|
|
2626
|
+
const lineHeight = px(cs.lineHeight) || px(cs.fontSize) * 1.2;
|
|
2627
|
+
const contentH = btn.getBoundingClientRect().height - px(cs.borderTopWidth) - px(cs.borderBottomWidth);
|
|
2628
|
+
const impliedY = Math.round(Math.max(0, (contentH - lineHeight) / 2));
|
|
2629
|
+
const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom), impliedY);
|
|
2630
|
+
const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
|
|
2631
|
+
return {
|
|
2632
|
+
radius: radius || "10px",
|
|
2633
|
+
padding: `${padY}px ${padX}px`,
|
|
2634
|
+
fontFamily: cs.fontFamily || "",
|
|
2635
|
+
fontSize: cs.fontSize || "",
|
|
2636
|
+
fontWeight: cs.fontWeight || "",
|
|
2637
|
+
letterSpacing: cs.letterSpacing || "",
|
|
2638
|
+
textTransform: cs.textTransform || ""
|
|
2639
|
+
};
|
|
1868
2640
|
}
|
|
1869
2641
|
var mounted = /* @__PURE__ */ new Map();
|
|
1870
2642
|
function findTemplateSection(id) {
|
|
@@ -1880,6 +2652,19 @@ function findPlacementAnchor(id, exclude) {
|
|
|
1880
2652
|
for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
|
|
1881
2653
|
if (el === exclude) continue;
|
|
1882
2654
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
2655
|
+
if (isChromeSection2(el)) return null;
|
|
2656
|
+
return el;
|
|
2657
|
+
}
|
|
2658
|
+
return null;
|
|
2659
|
+
}
|
|
2660
|
+
function findFooterSection() {
|
|
2661
|
+
const byId = findTemplateSection("footer");
|
|
2662
|
+
if (byId) return byId;
|
|
2663
|
+
for (const el of Array.from(
|
|
2664
|
+
document.querySelectorAll("footer[data-ohw-section]")
|
|
2665
|
+
).reverse()) {
|
|
2666
|
+
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
2667
|
+
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
1883
2668
|
return el;
|
|
1884
2669
|
}
|
|
1885
2670
|
return null;
|
|
@@ -1906,7 +2691,7 @@ function placeContainer(container, entry) {
|
|
|
1906
2691
|
return;
|
|
1907
2692
|
}
|
|
1908
2693
|
}
|
|
1909
|
-
const footer =
|
|
2694
|
+
const footer = findFooterSection();
|
|
1910
2695
|
if (footer) {
|
|
1911
2696
|
footer.insertAdjacentElement("beforebegin", container);
|
|
1912
2697
|
} else {
|
|
@@ -1915,18 +2700,18 @@ function placeContainer(container, entry) {
|
|
|
1915
2700
|
}
|
|
1916
2701
|
function syncRemovedSections(state) {
|
|
1917
2702
|
const removed = new Set(state.removed ?? []);
|
|
1918
|
-
for (const el of document.querySelectorAll(`[${
|
|
2703
|
+
for (const el of document.querySelectorAll(`[${REMOVED_ATTR2}]`)) {
|
|
1919
2704
|
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
1920
2705
|
if (!removed.has(id)) {
|
|
1921
2706
|
el.style.removeProperty("display");
|
|
1922
|
-
el.removeAttribute(
|
|
2707
|
+
el.removeAttribute(REMOVED_ATTR2);
|
|
1923
2708
|
}
|
|
1924
2709
|
}
|
|
1925
2710
|
for (const id of removed) {
|
|
1926
2711
|
const section = findTemplateSection(id);
|
|
1927
2712
|
if (section && !section.hasAttribute(REPLACED_ATTR)) {
|
|
1928
2713
|
section.style.display = "none";
|
|
1929
|
-
section.setAttribute(
|
|
2714
|
+
section.setAttribute(REMOVED_ATTR2, "");
|
|
1930
2715
|
}
|
|
1931
2716
|
}
|
|
1932
2717
|
}
|
|
@@ -1941,9 +2726,9 @@ function syncTemplateHidden(state, pageHasSections) {
|
|
|
1941
2726
|
if (!hide) return;
|
|
1942
2727
|
for (const el of Array.from(document.querySelectorAll("[data-ohw-section]"))) {
|
|
1943
2728
|
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
1944
|
-
if (
|
|
2729
|
+
if (isChromeSection2(el)) continue;
|
|
1945
2730
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
1946
|
-
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(
|
|
2731
|
+
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR2)) continue;
|
|
1947
2732
|
el.style.display = "none";
|
|
1948
2733
|
el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
|
|
1949
2734
|
}
|
|
@@ -1967,18 +2752,23 @@ function syncReplacedOriginals(state) {
|
|
|
1967
2752
|
}
|
|
1968
2753
|
}
|
|
1969
2754
|
var sectionOrderIndex = /* @__PURE__ */ new Map();
|
|
2755
|
+
var removedSectionIds = /* @__PURE__ */ new Set();
|
|
1970
2756
|
function setAiSectionOrder(raw, currentPath) {
|
|
1971
2757
|
const next = /* @__PURE__ */ new Map();
|
|
2758
|
+
const removed = /* @__PURE__ */ new Set();
|
|
1972
2759
|
if (raw) {
|
|
1973
2760
|
try {
|
|
1974
2761
|
const entries = JSON.parse(raw);
|
|
1975
2762
|
for (const entry of entries) {
|
|
1976
|
-
if (
|
|
2763
|
+
if (entry.pagePath && entry.pagePath !== currentPath) continue;
|
|
2764
|
+
next.set(entry.instanceId, entry.order);
|
|
2765
|
+
if (entry.removed) removed.add(entry.instanceId);
|
|
1977
2766
|
}
|
|
1978
2767
|
} catch {
|
|
1979
2768
|
}
|
|
1980
2769
|
}
|
|
1981
2770
|
sectionOrderIndex = next;
|
|
2771
|
+
removedSectionIds = removed;
|
|
1982
2772
|
}
|
|
1983
2773
|
function applyExplicitOrder(entries) {
|
|
1984
2774
|
if (sectionOrderIndex.size === 0) return entries;
|
|
@@ -2014,11 +2804,23 @@ function orderByChain(sections) {
|
|
|
2014
2804
|
for (const root of roots) visit(root);
|
|
2015
2805
|
return out.length === sections.length ? out : sections;
|
|
2016
2806
|
}
|
|
2807
|
+
function syncSoftRemovedGenerated() {
|
|
2808
|
+
for (const [id, section] of mounted) {
|
|
2809
|
+
const el = section.container;
|
|
2810
|
+
if (removedSectionIds.has(id)) {
|
|
2811
|
+
el.style.display = "none";
|
|
2812
|
+
el.setAttribute(REMOVED_ATTR, "");
|
|
2813
|
+
} else if (el.hasAttribute(REMOVED_ATTR)) {
|
|
2814
|
+
el.style.removeProperty("display");
|
|
2815
|
+
el.removeAttribute(REMOVED_ATTR);
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2017
2819
|
function applyAiSectionsToDom(state, options) {
|
|
2018
2820
|
if (typeof document === "undefined") return;
|
|
2019
2821
|
const brandOverride = deriveBrandOverride();
|
|
2020
2822
|
const templateBrand = deriveTemplateBrand();
|
|
2021
|
-
const
|
|
2823
|
+
const templateButtonStyle = deriveTemplateButtonStyle();
|
|
2022
2824
|
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2023
2825
|
const pagePath = window.location.pathname;
|
|
2024
2826
|
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
@@ -2058,7 +2860,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2058
2860
|
{
|
|
2059
2861
|
tree: entry.tree,
|
|
2060
2862
|
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2061
|
-
|
|
2863
|
+
buttonStyle: templateButtonStyle,
|
|
2062
2864
|
resolveMedia,
|
|
2063
2865
|
editKeyPrefix: `ai.${entry.id}`
|
|
2064
2866
|
}
|
|
@@ -2081,6 +2883,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2081
2883
|
syncReplacedOriginals(state);
|
|
2082
2884
|
syncRemovedSections(state);
|
|
2083
2885
|
syncTemplateHidden(state, pageSections.length > 0);
|
|
2886
|
+
syncSoftRemovedGenerated();
|
|
2084
2887
|
}
|
|
2085
2888
|
|
|
2086
2889
|
// src/useLinkHrefGuardian.ts
|
|
@@ -7810,6 +8613,7 @@ function MediaOverlay({
|
|
|
7810
8613
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
7811
8614
|
);
|
|
7812
8615
|
}, [isVideo]);
|
|
8616
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
7813
8617
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
7814
8618
|
const box = {
|
|
7815
8619
|
position: "fixed",
|
|
@@ -7939,17 +8743,17 @@ function MediaOverlay({
|
|
|
7939
8743
|
},
|
|
7940
8744
|
children: [
|
|
7941
8745
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7942
|
-
|
|
8746
|
+
replaceLabel
|
|
7943
8747
|
]
|
|
7944
8748
|
}
|
|
7945
8749
|
),
|
|
7946
|
-
replaceMode
|
|
8750
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
|
|
7947
8751
|
Button,
|
|
7948
8752
|
{
|
|
7949
8753
|
"data-ohw-media-overlay": "",
|
|
7950
8754
|
variant: "outline",
|
|
7951
8755
|
size: "sm",
|
|
7952
|
-
"aria-label":
|
|
8756
|
+
"aria-label": replaceLabel,
|
|
7953
8757
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
7954
8758
|
style: {
|
|
7955
8759
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -7972,7 +8776,7 @@ function MediaOverlay({
|
|
|
7972
8776
|
},
|
|
7973
8777
|
children: [
|
|
7974
8778
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7975
|
-
replaceMode === "full" ?
|
|
8779
|
+
replaceMode === "full" ? replaceLabel : null
|
|
7976
8780
|
]
|
|
7977
8781
|
}
|
|
7978
8782
|
)
|
|
@@ -8026,234 +8830,22 @@ function CarouselOverlay({
|
|
|
8026
8830
|
onClick: (e) => {
|
|
8027
8831
|
e.stopPropagation();
|
|
8028
8832
|
onEdit(hover.key);
|
|
8029
|
-
},
|
|
8030
|
-
children: [
|
|
8031
|
-
/* @__PURE__ */ jsx16(GalleryHorizontal, { size: 14 }),
|
|
8032
|
-
"Edit gallery"
|
|
8033
|
-
]
|
|
8034
|
-
}
|
|
8035
|
-
)
|
|
8036
|
-
}
|
|
8037
|
-
);
|
|
8038
|
-
}
|
|
8039
|
-
|
|
8040
|
-
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
8041
|
-
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
|
|
8042
|
-
import { Check, X } from "lucide-react";
|
|
8043
|
-
|
|
8044
|
-
// src/lib/sections.ts
|
|
8045
|
-
var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
8046
|
-
function isChromeSection(el) {
|
|
8047
|
-
return el.matches("header, nav, footer, aside");
|
|
8048
|
-
}
|
|
8049
|
-
function titleCaseSectionId(id) {
|
|
8050
|
-
return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
8051
|
-
}
|
|
8052
|
-
function parseSectionsFromRoot(root) {
|
|
8053
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8054
|
-
const sections = [];
|
|
8055
|
-
for (const el of root.querySelectorAll("[data-ohw-section]")) {
|
|
8056
|
-
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
8057
|
-
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
8058
|
-
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
8059
|
-
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
8060
|
-
continue;
|
|
8061
|
-
seen.add(id);
|
|
8062
|
-
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
8063
|
-
sections.push({ id, label });
|
|
8064
|
-
}
|
|
8065
|
-
return sections;
|
|
8066
|
-
}
|
|
8067
|
-
function collectSectionsFromDom() {
|
|
8068
|
-
if (typeof document === "undefined") return [];
|
|
8069
|
-
return parseSectionsFromRoot(document);
|
|
8070
|
-
}
|
|
8071
|
-
function parseSectionsFromHtml(html) {
|
|
8072
|
-
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
8073
|
-
return parseSectionsFromRoot(doc);
|
|
8074
|
-
}
|
|
8075
|
-
|
|
8076
|
-
// src/lib/section-instances.ts
|
|
8077
|
-
var SECTION_ORDER_KEY = "__ohw_section_order";
|
|
8078
|
-
var REMOVED_ATTR2 = "data-ohw-section-removed";
|
|
8079
|
-
function isRemovedSection(el) {
|
|
8080
|
-
return el.hasAttribute(REMOVED_ATTR2);
|
|
8081
|
-
}
|
|
8082
|
-
function topLevelSections() {
|
|
8083
|
-
return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
8084
|
-
(el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isRemovedSection(el)
|
|
8085
|
-
);
|
|
8086
|
-
}
|
|
8087
|
-
function instanceIdOf(el) {
|
|
8088
|
-
return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
|
|
8089
|
-
}
|
|
8090
|
-
function findByInstanceId(instanceId) {
|
|
8091
|
-
const escapedId = CSS.escape(instanceId);
|
|
8092
|
-
return document.querySelector(`[data-ohw-instance="${escapedId}"]`) ?? document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
|
|
8093
|
-
}
|
|
8094
|
-
function planSectionMove(instanceId, targetIndex, currentPath) {
|
|
8095
|
-
const sections = topLevelSections();
|
|
8096
|
-
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
8097
|
-
if (index === -1) return null;
|
|
8098
|
-
const dragged = sections[index];
|
|
8099
|
-
const others = sections.filter((_, i) => i !== index);
|
|
8100
|
-
const clamped = Math.max(0, Math.min(targetIndex, others.length));
|
|
8101
|
-
const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
|
|
8102
|
-
return reordered.map((el, order) => ({
|
|
8103
|
-
instanceId: instanceIdOf(el),
|
|
8104
|
-
type: el.getAttribute("data-ohw-section") ?? "",
|
|
8105
|
-
order,
|
|
8106
|
-
pagePath: currentPath
|
|
8107
|
-
}));
|
|
8108
|
-
}
|
|
8109
|
-
function moveSectionInstance(instanceId, direction, currentPath) {
|
|
8110
|
-
const sections = topLevelSections();
|
|
8111
|
-
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
8112
|
-
if (index === -1) return null;
|
|
8113
|
-
const siblingIndex = direction === "up" ? index - 1 : index + 1;
|
|
8114
|
-
if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
|
|
8115
|
-
const entries = planSectionMove(instanceId, siblingIndex, currentPath);
|
|
8116
|
-
if (!entries) return null;
|
|
8117
|
-
applyPersistedOrder(entries);
|
|
8118
|
-
return entries;
|
|
8119
|
-
}
|
|
8120
|
-
function syncRemovedFlags(entries) {
|
|
8121
|
-
const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
|
|
8122
|
-
document.querySelectorAll(`[${REMOVED_ATTR2}]`).forEach((el) => {
|
|
8123
|
-
if (!removedIds.has(instanceIdOf(el))) {
|
|
8124
|
-
el.style.removeProperty("display");
|
|
8125
|
-
el.removeAttribute(REMOVED_ATTR2);
|
|
8126
|
-
}
|
|
8127
|
-
});
|
|
8128
|
-
for (const id of removedIds) {
|
|
8129
|
-
const el = findByInstanceId(id);
|
|
8130
|
-
if (el) {
|
|
8131
|
-
el.style.display = "none";
|
|
8132
|
-
el.setAttribute(REMOVED_ATTR2, "");
|
|
8833
|
+
},
|
|
8834
|
+
children: [
|
|
8835
|
+
/* @__PURE__ */ jsx16(GalleryHorizontal, { size: 14 }),
|
|
8836
|
+
"Edit gallery"
|
|
8837
|
+
]
|
|
8838
|
+
}
|
|
8839
|
+
)
|
|
8133
8840
|
}
|
|
8134
|
-
}
|
|
8135
|
-
}
|
|
8136
|
-
function applyPersistedOrder(entries) {
|
|
8137
|
-
syncRemovedFlags(entries);
|
|
8138
|
-
if (entries.length === 0) return;
|
|
8139
|
-
const sections = topLevelSections();
|
|
8140
|
-
if (sections.length === 0) return;
|
|
8141
|
-
const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
|
|
8142
|
-
const ordered = [...sections].sort((a, b) => {
|
|
8143
|
-
const aOrder = orderIndex.get(instanceIdOf(a));
|
|
8144
|
-
const bOrder = orderIndex.get(instanceIdOf(b));
|
|
8145
|
-
if (aOrder === void 0 && bOrder === void 0) return 0;
|
|
8146
|
-
if (aOrder === void 0) return 1;
|
|
8147
|
-
if (bOrder === void 0) return -1;
|
|
8148
|
-
return aOrder - bOrder;
|
|
8149
|
-
});
|
|
8150
|
-
let prev = null;
|
|
8151
|
-
for (const el of ordered) {
|
|
8152
|
-
if (prev) prev.after(el);
|
|
8153
|
-
prev = el;
|
|
8154
|
-
}
|
|
8155
|
-
}
|
|
8156
|
-
function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
|
|
8157
|
-
if (!findByInstanceId(instanceId)) return null;
|
|
8158
|
-
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
8159
|
-
const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
8160
|
-
(el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
|
|
8161
8841
|
);
|
|
8162
|
-
allSections.forEach((el, order) => {
|
|
8163
|
-
const id = instanceIdOf(el);
|
|
8164
|
-
if (!byId.has(id)) {
|
|
8165
|
-
byId.set(id, { instanceId: id, type: el.getAttribute("data-ohw-section") ?? "", order, pagePath: currentPath });
|
|
8166
|
-
}
|
|
8167
|
-
});
|
|
8168
|
-
const target = byId.get(instanceId);
|
|
8169
|
-
if (!target) return null;
|
|
8170
|
-
byId.set(instanceId, { ...target, removed });
|
|
8171
|
-
const entries = Array.from(byId.values());
|
|
8172
|
-
applyPersistedOrder(entries);
|
|
8173
|
-
return entries;
|
|
8174
|
-
}
|
|
8175
|
-
function deleteSectionInstance(instanceId, currentPath, existingEntries) {
|
|
8176
|
-
return setSectionRemoved(instanceId, currentPath, existingEntries, true);
|
|
8177
|
-
}
|
|
8178
|
-
function restoreSectionInstance(instanceId, currentPath, existingEntries) {
|
|
8179
|
-
return setSectionRemoved(instanceId, currentPath, existingEntries, false);
|
|
8180
|
-
}
|
|
8181
|
-
function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
|
|
8182
|
-
const original = findByInstanceId(instanceId);
|
|
8183
|
-
if (!original) return null;
|
|
8184
|
-
const clone = original.cloneNode(true);
|
|
8185
|
-
clone.setAttribute("data-ohw-instance", newId);
|
|
8186
|
-
const keyRekeys = rekeySectionSubtree(clone, newId);
|
|
8187
|
-
original.insertAdjacentElement("afterend", clone);
|
|
8188
|
-
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
8189
|
-
const entries = topLevelSections().map((el, order) => {
|
|
8190
|
-
const id = instanceIdOf(el);
|
|
8191
|
-
return {
|
|
8192
|
-
instanceId: id,
|
|
8193
|
-
type: el.getAttribute("data-ohw-section") ?? "",
|
|
8194
|
-
order,
|
|
8195
|
-
pagePath: currentPath,
|
|
8196
|
-
...byId.get(id)?.removed ? { removed: true } : {}
|
|
8197
|
-
};
|
|
8198
|
-
});
|
|
8199
|
-
applyPersistedOrder(entries);
|
|
8200
|
-
return { entries, keyRekeys };
|
|
8201
|
-
}
|
|
8202
|
-
function newInstanceId() {
|
|
8203
|
-
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
8204
|
-
}
|
|
8205
|
-
function getPageSectionOrderEntries(raw, currentPath) {
|
|
8206
|
-
if (!raw) return [];
|
|
8207
|
-
try {
|
|
8208
|
-
const entries = JSON.parse(raw);
|
|
8209
|
-
return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
|
|
8210
|
-
} catch {
|
|
8211
|
-
return [];
|
|
8212
|
-
}
|
|
8213
|
-
}
|
|
8214
|
-
function rekeySectionSubtree(root, instanceId) {
|
|
8215
|
-
const suffix = `::${instanceId}`;
|
|
8216
|
-
const pairs = [];
|
|
8217
|
-
const rekey = (el, attr) => {
|
|
8218
|
-
const current = el.getAttribute(attr);
|
|
8219
|
-
if (!current) return;
|
|
8220
|
-
const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
|
|
8221
|
-
const next = `${base}${suffix}`;
|
|
8222
|
-
el.setAttribute(attr, next);
|
|
8223
|
-
pairs.push({ from: current, to: next });
|
|
8224
|
-
};
|
|
8225
|
-
if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
|
|
8226
|
-
if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
|
|
8227
|
-
root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
|
|
8228
|
-
root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
|
|
8229
|
-
return pairs;
|
|
8230
|
-
}
|
|
8231
|
-
function initSectionInstancesFromContent(content, currentPath) {
|
|
8232
|
-
document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
|
|
8233
|
-
el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
|
|
8234
|
-
});
|
|
8235
|
-
const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
|
|
8236
|
-
for (const entry of entries) {
|
|
8237
|
-
if (entry.instanceId === entry.type) continue;
|
|
8238
|
-
if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
|
|
8239
|
-
const original = document.querySelector(
|
|
8240
|
-
`[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
|
|
8241
|
-
);
|
|
8242
|
-
if (!original) continue;
|
|
8243
|
-
const clone = original.cloneNode(true);
|
|
8244
|
-
clone.setAttribute("data-ohw-instance", entry.instanceId);
|
|
8245
|
-
rekeySectionSubtree(clone, entry.instanceId);
|
|
8246
|
-
original.insertAdjacentElement("afterend", clone);
|
|
8247
|
-
}
|
|
8248
|
-
applyPersistedOrder(entries);
|
|
8249
8842
|
}
|
|
8250
8843
|
|
|
8251
8844
|
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
8845
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
|
|
8846
|
+
import { Check, X } from "lucide-react";
|
|
8252
8847
|
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
8253
|
-
|
|
8254
|
-
const escaped = CSS.escape(instanceId);
|
|
8255
|
-
return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
|
|
8256
|
-
}
|
|
8848
|
+
var findSectionElement = findByInstanceId;
|
|
8257
8849
|
function readRect(instanceId) {
|
|
8258
8850
|
const el = findSectionElement(instanceId);
|
|
8259
8851
|
if (!el) return null;
|
|
@@ -8293,7 +8885,7 @@ function useLiveSectionRect(sectionId) {
|
|
|
8293
8885
|
}
|
|
8294
8886
|
function computeSectionBoundaryFlags(instanceId) {
|
|
8295
8887
|
const topLevel = topLevelSections();
|
|
8296
|
-
const index = topLevel.findIndex((el) => (el
|
|
8888
|
+
const index = topLevel.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
8297
8889
|
if (index === -1) return { isFirst: true, isLast: true };
|
|
8298
8890
|
return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
|
|
8299
8891
|
}
|
|
@@ -8377,18 +8969,20 @@ function AiSectionOverlay({
|
|
|
8377
8969
|
selectedIdRef.current = selectedId;
|
|
8378
8970
|
const report = useCallback2(
|
|
8379
8971
|
(el) => {
|
|
8972
|
+
const labelSrc = el ? sectionElementOf(el) : null;
|
|
8380
8973
|
postToParent2({
|
|
8381
8974
|
type: "ow:section-selected",
|
|
8382
|
-
sectionId: el ? el
|
|
8383
|
-
sectionLabel:
|
|
8975
|
+
sectionId: el ? instanceIdOf(el) || null : null,
|
|
8976
|
+
sectionLabel: labelSrc ? labelSrc.dataset.ohwSectionLabel ?? titleCaseSectionId(labelSrc.dataset.ohwSection ?? "") : null
|
|
8384
8977
|
});
|
|
8385
8978
|
},
|
|
8386
8979
|
[postToParent2]
|
|
8387
8980
|
);
|
|
8388
8981
|
const selectFromElement = useCallback2(
|
|
8389
8982
|
(el, options) => {
|
|
8390
|
-
const
|
|
8391
|
-
const
|
|
8983
|
+
const inner = el?.closest("[data-ohw-section]") ?? null;
|
|
8984
|
+
const sectionEl = inner ? movableUnit(inner) : null;
|
|
8985
|
+
const id = sectionEl ? instanceIdOf(sectionEl) || null : null;
|
|
8392
8986
|
if (id === selectedIdRef.current) return;
|
|
8393
8987
|
setSelectedId(id);
|
|
8394
8988
|
if (options?.report !== false) report(sectionEl);
|
|
@@ -8454,7 +9048,8 @@ function AiSectionOverlay({
|
|
|
8454
9048
|
return;
|
|
8455
9049
|
}
|
|
8456
9050
|
const sec = t.closest("[data-ohw-section]");
|
|
8457
|
-
|
|
9051
|
+
const unit = sec ? movableUnit(sec) : null;
|
|
9052
|
+
setHoveredId(unit ? instanceIdOf(unit) || null : null);
|
|
8458
9053
|
};
|
|
8459
9054
|
const onLeave = () => setHoveredId(null);
|
|
8460
9055
|
document.addEventListener("mousemove", onMove, { passive: true });
|
|
@@ -12965,6 +13560,7 @@ function readLogoSizeState(content, placement) {
|
|
|
12965
13560
|
function getLogoElement(el) {
|
|
12966
13561
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
12967
13562
|
if (marked) return marked;
|
|
13563
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
12968
13564
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
12969
13565
|
if (!root) return null;
|
|
12970
13566
|
const anchor = el.closest("a");
|
|
@@ -14037,15 +14633,17 @@ function useSectionDrag({
|
|
|
14037
14633
|
clearSectionDragVisuals();
|
|
14038
14634
|
return;
|
|
14039
14635
|
}
|
|
14040
|
-
const orderJson = JSON.stringify(
|
|
14636
|
+
const orderJson = JSON.stringify(
|
|
14637
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
14638
|
+
);
|
|
14041
14639
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
14042
14640
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
14043
14641
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
14044
|
-
applyPersistedOrder(
|
|
14642
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
14045
14643
|
clearSectionDragVisuals();
|
|
14046
14644
|
requestAnimationFrame(() => {
|
|
14047
14645
|
if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
|
|
14048
|
-
applyPersistedOrder(
|
|
14646
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
14049
14647
|
}
|
|
14050
14648
|
requestAnimationFrame(() => {
|
|
14051
14649
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -14078,8 +14676,9 @@ function useSectionDrag({
|
|
|
14078
14676
|
const target = e.target;
|
|
14079
14677
|
if (!(target instanceof HTMLElement)) return;
|
|
14080
14678
|
if (target.closest(SECTION_DRAG_EXCLUDED_SELECTOR)) return;
|
|
14081
|
-
const
|
|
14082
|
-
if (!
|
|
14679
|
+
const inner = target.closest("[data-ohw-section]");
|
|
14680
|
+
if (!inner || isChromeSection(inner) || inner.dataset.ohwSection === "footer") return;
|
|
14681
|
+
const sectionEl = movableUnit(inner);
|
|
14083
14682
|
if (!topLevelSections().includes(sectionEl)) return;
|
|
14084
14683
|
startSectionPressDrag(sectionEl, e.clientX, e.clientY, e.pointerId);
|
|
14085
14684
|
};
|
|
@@ -14346,6 +14945,9 @@ function collectEditableNodes(extraContent, root = document) {
|
|
|
14346
14945
|
if (el.dataset.ohwEditable === "link") {
|
|
14347
14946
|
return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref3(el) };
|
|
14348
14947
|
}
|
|
14948
|
+
if (el.dataset.ohwEditable === "map") {
|
|
14949
|
+
return { key: el.dataset.ohwKey ?? "", type: "map", text: el.dataset.ohwMapQuery ?? "" };
|
|
14950
|
+
}
|
|
14349
14951
|
return {
|
|
14350
14952
|
key: el.dataset.ohwKey ?? "",
|
|
14351
14953
|
type: el.dataset.ohwEditable ?? "text",
|
|
@@ -14908,21 +15510,10 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
14908
15510
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
14909
15511
|
};
|
|
14910
15512
|
}
|
|
14911
|
-
function
|
|
14912
|
-
|
|
14913
|
-
const
|
|
14914
|
-
|
|
14915
|
-
return { effectiveInsertAfter, insertBefore };
|
|
14916
|
-
}
|
|
14917
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
14918
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
14919
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
14920
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
14921
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
14922
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
14923
|
-
}
|
|
14924
|
-
if (!anchorEl) return null;
|
|
14925
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
15513
|
+
function resolveEntryAnchor(entry) {
|
|
15514
|
+
if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
|
|
15515
|
+
const parsed = parseSchedulingInsertAfter(entry.insertAfter);
|
|
15516
|
+
return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
|
|
14926
15517
|
}
|
|
14927
15518
|
function schedulingMountDepth(insertAfter) {
|
|
14928
15519
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -14939,8 +15530,7 @@ function getPageSchedulingEntries(raw) {
|
|
|
14939
15530
|
}
|
|
14940
15531
|
}
|
|
14941
15532
|
function isSchedulingWidgetMissing(entry) {
|
|
14942
|
-
|
|
14943
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
15533
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
|
|
14944
15534
|
}
|
|
14945
15535
|
function hasMissingSchedulingWidgets(entries) {
|
|
14946
15536
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -14978,18 +15568,18 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
|
|
|
14978
15568
|
} catch {
|
|
14979
15569
|
}
|
|
14980
15570
|
}
|
|
14981
|
-
function mountSchedulingWidget(
|
|
14982
|
-
const
|
|
14983
|
-
const sectionId = schedulingSectionId(
|
|
15571
|
+
function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
|
|
15572
|
+
const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
|
|
15573
|
+
const sectionId = schedulingSectionId(widgetId);
|
|
14984
15574
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
14985
|
-
const
|
|
14986
|
-
if (!
|
|
15575
|
+
const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
|
|
15576
|
+
if (!anchorEl) return false;
|
|
15577
|
+
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14987
15578
|
const container = document.createElement("div");
|
|
14988
15579
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
14989
|
-
container.dataset.ohwSection = sectionId;
|
|
14990
15580
|
container.dataset.ohwInstance = sectionId;
|
|
14991
|
-
if (
|
|
14992
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
15581
|
+
if (beforeId) {
|
|
15582
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
|
|
14993
15583
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
14994
15584
|
if (!beforePoint) return false;
|
|
14995
15585
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -15000,20 +15590,26 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
15000
15590
|
}
|
|
15001
15591
|
tail.insertAdjacentElement("afterend", container);
|
|
15002
15592
|
}
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
|
|
15593
|
+
try {
|
|
15594
|
+
const root = createRoot2(container);
|
|
15595
|
+
schedulingRoots.set(container, root);
|
|
15596
|
+
flushSync2(() => {
|
|
15597
|
+
root.render(
|
|
15598
|
+
/* @__PURE__ */ jsx33(
|
|
15599
|
+
SchedulingWidget,
|
|
15600
|
+
{
|
|
15601
|
+
notifyOnConnect,
|
|
15602
|
+
initialScheduleId: scheduleId,
|
|
15603
|
+
insertAfter: widgetId
|
|
15604
|
+
}
|
|
15605
|
+
)
|
|
15606
|
+
);
|
|
15607
|
+
});
|
|
15608
|
+
} catch (err) {
|
|
15609
|
+
console.error("[ow:scheduling] render threw", err);
|
|
15610
|
+
container.remove();
|
|
15611
|
+
return false;
|
|
15612
|
+
}
|
|
15017
15613
|
const tracker = getSectionsTracker();
|
|
15018
15614
|
let sections = [];
|
|
15019
15615
|
try {
|
|
@@ -15021,10 +15617,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
15021
15617
|
} catch {
|
|
15022
15618
|
}
|
|
15023
15619
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
15024
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
15620
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
|
|
15025
15621
|
sections.push({
|
|
15026
15622
|
type: "scheduling",
|
|
15027
|
-
insertAfter:
|
|
15623
|
+
insertAfter: widgetId,
|
|
15624
|
+
anchorId,
|
|
15625
|
+
beforeId: beforeId ?? null,
|
|
15028
15626
|
pagePath: window.location.pathname,
|
|
15029
15627
|
...scheduleId ? { scheduleId } : {}
|
|
15030
15628
|
});
|
|
@@ -15038,7 +15636,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
15038
15636
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
15039
15637
|
const entry = pending[i];
|
|
15040
15638
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
15041
|
-
|
|
15639
|
+
const { anchorId, beforeId } = resolveEntryAnchor(entry);
|
|
15640
|
+
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
15042
15641
|
pending.splice(i, 1);
|
|
15043
15642
|
}
|
|
15044
15643
|
}
|
|
@@ -15130,7 +15729,7 @@ var EDITOR_CHROME_SELECTOR = '[data-ohw-toolbar], [data-ohw-form-toolbar], [data
|
|
|
15130
15729
|
function isOverEditorChrome(x, y) {
|
|
15131
15730
|
return document.elementsFromPoint(x, y).some((el) => el instanceof HTMLElement && el.matches(EDITOR_CHROME_SELECTOR));
|
|
15132
15731
|
}
|
|
15133
|
-
var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="icon"]):not([data-ohw-editable="form"])';
|
|
15732
|
+
var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="icon"]):not([data-ohw-editable="form"]):not([data-ohw-editable="map"])';
|
|
15134
15733
|
function getVideoEl2(el) {
|
|
15135
15734
|
return el instanceof HTMLVideoElement ? el : el.querySelector("video");
|
|
15136
15735
|
}
|
|
@@ -15186,6 +15785,12 @@ function applyVideoSettingNode(key, val) {
|
|
|
15186
15785
|
});
|
|
15187
15786
|
return true;
|
|
15188
15787
|
}
|
|
15788
|
+
function applyMapQuery(el, val) {
|
|
15789
|
+
if (!(el instanceof HTMLIFrameElement)) return;
|
|
15790
|
+
const nextSrc = `https://www.google.com/maps?q=${encodeURIComponent(val)}&output=embed`;
|
|
15791
|
+
if (el.src !== nextSrc) el.src = nextSrc;
|
|
15792
|
+
el.setAttribute("data-ohw-map-query", val);
|
|
15793
|
+
}
|
|
15189
15794
|
function applyLinkByKey(key, val) {
|
|
15190
15795
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
15191
15796
|
if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
|
|
@@ -15196,6 +15801,11 @@ function applyLinkByKey(key, val) {
|
|
|
15196
15801
|
hrefAnchors.forEach((el) => applyLinkHref(el, val));
|
|
15197
15802
|
}
|
|
15198
15803
|
}
|
|
15804
|
+
function isInsideLinkEditor(target) {
|
|
15805
|
+
return Boolean(
|
|
15806
|
+
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
|
|
15807
|
+
);
|
|
15808
|
+
}
|
|
15199
15809
|
function isInsideFloatingPanel(target) {
|
|
15200
15810
|
return Boolean(target.closest("[data-ohw-floating-panel]"));
|
|
15201
15811
|
}
|
|
@@ -15203,11 +15813,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
|
|
|
15203
15813
|
const el = document.elementFromPoint(clientX, clientY);
|
|
15204
15814
|
return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
|
|
15205
15815
|
}
|
|
15206
|
-
function isInsideLinkEditor(target) {
|
|
15207
|
-
return Boolean(
|
|
15208
|
-
target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
|
|
15209
|
-
);
|
|
15210
|
-
}
|
|
15211
15816
|
function getHrefKeyFromElement(el) {
|
|
15212
15817
|
if (!el) return null;
|
|
15213
15818
|
const anchor = el.closest("[data-ohw-href-key]");
|
|
@@ -15466,7 +16071,7 @@ function getNavigationSelectionParent(el) {
|
|
|
15466
16071
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
15467
16072
|
return getFooterLinksContainer();
|
|
15468
16073
|
}
|
|
15469
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
16074
|
+
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup2(el)) {
|
|
15470
16075
|
return getNavigationRoot(el);
|
|
15471
16076
|
}
|
|
15472
16077
|
return null;
|
|
@@ -15681,7 +16286,6 @@ var ICONS = {
|
|
|
15681
16286
|
insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
|
|
15682
16287
|
insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
|
|
15683
16288
|
};
|
|
15684
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
15685
16289
|
var SELECTION_CHROME_GAP2 = 4;
|
|
15686
16290
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
15687
16291
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -16061,6 +16665,7 @@ function StateToggle({
|
|
|
16061
16665
|
);
|
|
16062
16666
|
}
|
|
16063
16667
|
var contentCache = /* @__PURE__ */ new Map();
|
|
16668
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
16064
16669
|
var brandingCache = /* @__PURE__ */ new Map();
|
|
16065
16670
|
var OHW_LOADER_STYLE = {
|
|
16066
16671
|
position: "fixed",
|
|
@@ -16590,13 +17195,6 @@ function OhhwellsBridge() {
|
|
|
16590
17195
|
const [isItemDragging, setIsItemDragging] = useState13(false);
|
|
16591
17196
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
|
|
16592
17197
|
isFooterFrameSelectionRef.current = isFooterFrameSelection;
|
|
16593
|
-
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
16594
|
-
const floatingPanelOpenRef = useRef10(false);
|
|
16595
|
-
floatingPanelOpenRef.current = floatingPanel !== null;
|
|
16596
|
-
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
16597
|
-
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
16598
|
-
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
16599
|
-
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
16600
17198
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
|
|
16601
17199
|
const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
|
|
16602
17200
|
const footerDragRef = useRef10(null);
|
|
@@ -16614,6 +17212,13 @@ function OhhwellsBridge() {
|
|
|
16614
17212
|
const brandKitRef = useRef10("");
|
|
16615
17213
|
const stylesRef = useRef10("");
|
|
16616
17214
|
const pendingDeleteUndoRef = useRef10(null);
|
|
17215
|
+
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
17216
|
+
const floatingPanelOpenRef = useRef10(false);
|
|
17217
|
+
const setFloatingPanelRef = useRef10(setFloatingPanel);
|
|
17218
|
+
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
17219
|
+
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
17220
|
+
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
17221
|
+
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
16617
17222
|
const [sitePages, setSitePages] = useState13([]);
|
|
16618
17223
|
const [sectionsByPath, setSectionsByPath] = useState13({});
|
|
16619
17224
|
const sectionsPrefetchGenRef = useRef10(0);
|
|
@@ -16622,7 +17227,18 @@ function OhhwellsBridge() {
|
|
|
16622
17227
|
const linkPopoverOpenRef = useRef10(false);
|
|
16623
17228
|
const linkPopoverGraceUntilRef = useRef10(0);
|
|
16624
17229
|
setLinkPopoverRef.current = setLinkPopover;
|
|
17230
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
16625
17231
|
linkPopoverSessionRef.current = linkPopover;
|
|
17232
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
17233
|
+
useEffect13(() => {
|
|
17234
|
+
const syncViewport = () => {
|
|
17235
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
17236
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
17237
|
+
};
|
|
17238
|
+
syncViewport();
|
|
17239
|
+
window.addEventListener("resize", syncViewport);
|
|
17240
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
17241
|
+
}, []);
|
|
16626
17242
|
const {
|
|
16627
17243
|
navDragRef,
|
|
16628
17244
|
navDropSlots,
|
|
@@ -17947,6 +18563,7 @@ function OhhwellsBridge() {
|
|
|
17947
18563
|
}
|
|
17948
18564
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17949
18565
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
18566
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
17950
18567
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17951
18568
|
}
|
|
17952
18569
|
applyBrandChrome(content);
|
|
@@ -17954,11 +18571,11 @@ function OhhwellsBridge() {
|
|
|
17954
18571
|
for (const [key, val] of Object.entries(content)) {
|
|
17955
18572
|
if (key === "__ohw_sections") continue;
|
|
17956
18573
|
if (key === AI_SECTIONS_KEY) continue;
|
|
18574
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18575
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17957
18576
|
if (key === BRAND_KIT_KEY) continue;
|
|
17958
18577
|
if (key === STYLE_STORE_KEY) continue;
|
|
17959
18578
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17960
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17961
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17962
18579
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17963
18580
|
if (applyCarouselNode(key, val)) continue;
|
|
17964
18581
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17986,6 +18603,8 @@ function OhhwellsBridge() {
|
|
|
17986
18603
|
}
|
|
17987
18604
|
} else if (el.dataset.ohwEditable === "link") {
|
|
17988
18605
|
applyLinkHref(el, val);
|
|
18606
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
18607
|
+
applyMapQuery(el, val);
|
|
17989
18608
|
} else if (el.dataset.ohwEditable === "icon") {
|
|
17990
18609
|
applyIconMarkup(el, val);
|
|
17991
18610
|
} else if (el.dataset.ohwEditable === "form") {
|
|
@@ -18024,7 +18643,9 @@ function OhhwellsBridge() {
|
|
|
18024
18643
|
let cancelled = false;
|
|
18025
18644
|
setFetchState("loading");
|
|
18026
18645
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18027
|
-
|
|
18646
|
+
const initialPath = pathname;
|
|
18647
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
18648
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18028
18649
|
if (cancelled) return;
|
|
18029
18650
|
const content = data?.content ?? {};
|
|
18030
18651
|
const branding = Boolean(data?.showBranding);
|
|
@@ -18143,10 +18764,10 @@ function OhhwellsBridge() {
|
|
|
18143
18764
|
const applyFromCache = () => {
|
|
18144
18765
|
const content = contentCache.get(subdomain);
|
|
18145
18766
|
if (!content) return;
|
|
18146
|
-
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
18147
|
-
initSectionInstancesFromContent(content, window.location.pathname);
|
|
18148
18767
|
observer?.disconnect();
|
|
18149
18768
|
try {
|
|
18769
|
+
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
18770
|
+
initSectionInstancesFromContent(content, window.location.pathname);
|
|
18150
18771
|
applyBrandChrome(content);
|
|
18151
18772
|
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
18152
18773
|
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
@@ -18158,16 +18779,17 @@ function OhhwellsBridge() {
|
|
|
18158
18779
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18159
18780
|
}
|
|
18160
18781
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18782
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
18161
18783
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18162
18784
|
}
|
|
18163
18785
|
for (const [key, val] of Object.entries(content)) {
|
|
18164
18786
|
if (key === "__ohw_sections") continue;
|
|
18165
18787
|
if (key === AI_SECTIONS_KEY) continue;
|
|
18788
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18789
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18166
18790
|
if (key === BRAND_KIT_KEY) continue;
|
|
18167
18791
|
if (key === STYLE_STORE_KEY) continue;
|
|
18168
18792
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
18169
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18170
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18171
18793
|
if (applyVideoSettingNode(key, val)) continue;
|
|
18172
18794
|
if (applyCarouselNode(key, val)) continue;
|
|
18173
18795
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -18182,6 +18804,8 @@ function OhhwellsBridge() {
|
|
|
18182
18804
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
18183
18805
|
} else if (el.dataset.ohwEditable === "link") {
|
|
18184
18806
|
applyLinkHref(el, val);
|
|
18807
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
18808
|
+
applyMapQuery(el, val);
|
|
18185
18809
|
} else if (el.dataset.ohwEditable === "form") {
|
|
18186
18810
|
} else if (isIconMarkupValue(val)) {
|
|
18187
18811
|
} else if (el.innerHTML !== val) {
|
|
@@ -18213,6 +18837,17 @@ function OhhwellsBridge() {
|
|
|
18213
18837
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
18214
18838
|
};
|
|
18215
18839
|
applyFromCache();
|
|
18840
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18841
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18842
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18843
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18844
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18845
|
+
if (!data?.content) return;
|
|
18846
|
+
contentCache.set(subdomain, data.content);
|
|
18847
|
+
applyFromCache();
|
|
18848
|
+
}).catch(() => {
|
|
18849
|
+
});
|
|
18850
|
+
}
|
|
18216
18851
|
observer = new MutationObserver(scheduleApply);
|
|
18217
18852
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
18218
18853
|
return () => {
|
|
@@ -18237,6 +18872,10 @@ function OhhwellsBridge() {
|
|
|
18237
18872
|
deselectRef.current();
|
|
18238
18873
|
deactivateRef.current();
|
|
18239
18874
|
}, [pathname, isEditMode]);
|
|
18875
|
+
useEffect13(() => {
|
|
18876
|
+
if (!isEditMode) return;
|
|
18877
|
+
initSectionInstancesFromContent(editContentRef.current, pathname);
|
|
18878
|
+
}, [pathname, isEditMode]);
|
|
18240
18879
|
useEffect13(() => {
|
|
18241
18880
|
const contentForNav = () => {
|
|
18242
18881
|
if (isEditMode) return editContentRef.current;
|
|
@@ -18328,26 +18967,11 @@ function OhhwellsBridge() {
|
|
|
18328
18967
|
const t2 = setTimeout(measure, 500);
|
|
18329
18968
|
const ro = new ResizeObserver(schedule);
|
|
18330
18969
|
ro.observe(document.body);
|
|
18331
|
-
let lastWidth = window.innerWidth;
|
|
18332
|
-
let resizeTimers = [];
|
|
18333
|
-
const clearResizeTimers = () => {
|
|
18334
|
-
resizeTimers.forEach(clearTimeout);
|
|
18335
|
-
resizeTimers = [];
|
|
18336
|
-
};
|
|
18337
|
-
const handleResize = () => {
|
|
18338
|
-
if (window.innerWidth === lastWidth) return;
|
|
18339
|
-
lastWidth = window.innerWidth;
|
|
18340
|
-
clearResizeTimers();
|
|
18341
|
-
resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
|
|
18342
|
-
};
|
|
18343
|
-
window.addEventListener("resize", handleResize);
|
|
18344
18970
|
return () => {
|
|
18345
18971
|
clearTimeout(t1);
|
|
18346
18972
|
clearTimeout(t2);
|
|
18347
18973
|
if (raf != null) cancelAnimationFrame(raf);
|
|
18348
18974
|
ro.disconnect();
|
|
18349
|
-
clearResizeTimers();
|
|
18350
|
-
window.removeEventListener("resize", handleResize);
|
|
18351
18975
|
};
|
|
18352
18976
|
}, [pathname, isEditMode, postToParent2]);
|
|
18353
18977
|
useEffect13(() => {
|
|
@@ -18602,9 +19226,6 @@ function OhhwellsBridge() {
|
|
|
18602
19226
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
18603
19227
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
18604
19228
|
if (isInsideLinkEditor(target)) return;
|
|
18605
|
-
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
18606
|
-
clearMediaSelectionRef.current();
|
|
18607
|
-
}
|
|
18608
19229
|
if (isInsideFloatingPanel(target)) return;
|
|
18609
19230
|
if (target.closest("[data-ohw-form-toolbar]")) return;
|
|
18610
19231
|
if (target.closest(
|
|
@@ -18612,6 +19233,9 @@ function OhhwellsBridge() {
|
|
|
18612
19233
|
)) {
|
|
18613
19234
|
return;
|
|
18614
19235
|
}
|
|
19236
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
19237
|
+
clearMediaSelectionRef.current();
|
|
19238
|
+
}
|
|
18615
19239
|
{
|
|
18616
19240
|
const formEl = getFormElement(target);
|
|
18617
19241
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -18763,14 +19387,6 @@ function OhhwellsBridge() {
|
|
|
18763
19387
|
}
|
|
18764
19388
|
const clickedButton = findClosestButtonLike(target);
|
|
18765
19389
|
const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
|
|
18766
|
-
console.log("[click-debug]", {
|
|
18767
|
-
editableType: editable.dataset.ohwEditable,
|
|
18768
|
-
editableTag: editable.tagName,
|
|
18769
|
-
targetTag: target.tagName,
|
|
18770
|
-
clickedButtonTag: clickedButton?.tagName ?? null,
|
|
18771
|
-
buttonOnMedia,
|
|
18772
|
-
isMediaEditableEditable: isMediaEditable(editable)
|
|
18773
|
-
});
|
|
18774
19390
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
18775
19391
|
e.preventDefault();
|
|
18776
19392
|
e.stopPropagation();
|
|
@@ -18797,11 +19413,6 @@ function OhhwellsBridge() {
|
|
|
18797
19413
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
18798
19414
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
18799
19415
|
const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
|
|
18800
|
-
console.log("[click-debug 2]", {
|
|
18801
|
-
hrefLookupTargetTag: hrefLookupTarget.tagName,
|
|
18802
|
-
hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
|
|
18803
|
-
navAnchorTag: navAnchor?.tagName ?? null
|
|
18804
|
-
});
|
|
18805
19416
|
if (navAnchor) {
|
|
18806
19417
|
e.preventDefault();
|
|
18807
19418
|
e.stopPropagation();
|
|
@@ -18971,6 +19582,9 @@ function OhhwellsBridge() {
|
|
|
18971
19582
|
setHoveredItemRect(null);
|
|
18972
19583
|
hoveredNavContainerRef.current = null;
|
|
18973
19584
|
setHoveredNavContainerRect(null);
|
|
19585
|
+
siblingHintElRef.current = null;
|
|
19586
|
+
setSiblingHintRect(null);
|
|
19587
|
+
setSiblingHintRects([]);
|
|
18974
19588
|
return;
|
|
18975
19589
|
}
|
|
18976
19590
|
{
|
|
@@ -19089,7 +19703,6 @@ function OhhwellsBridge() {
|
|
|
19089
19703
|
hoveredNavContainerRef.current = null;
|
|
19090
19704
|
setHoveredNavContainerRect(null);
|
|
19091
19705
|
hoveredItemElRef.current = editable;
|
|
19092
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
19093
19706
|
}
|
|
19094
19707
|
}
|
|
19095
19708
|
}
|
|
@@ -19386,7 +19999,7 @@ function OhhwellsBridge() {
|
|
|
19386
19999
|
}
|
|
19387
20000
|
};
|
|
19388
20001
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
19389
|
-
if (linkPopoverOpenRef.current) {
|
|
20002
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19390
20003
|
if (hoveredImageRef.current) {
|
|
19391
20004
|
hoveredImageRef.current = null;
|
|
19392
20005
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -19751,8 +20364,7 @@ function OhhwellsBridge() {
|
|
|
19751
20364
|
};
|
|
19752
20365
|
const handleMouseMove = (e) => {
|
|
19753
20366
|
const { clientX, clientY } = e;
|
|
19754
|
-
if (
|
|
19755
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
20367
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
19756
20368
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
19757
20369
|
formHoverElRef.current = null;
|
|
19758
20370
|
setFormHoverRect(null);
|
|
@@ -19760,6 +20372,12 @@ function OhhwellsBridge() {
|
|
|
19760
20372
|
setHoveredItemRect(null);
|
|
19761
20373
|
hoveredNavContainerRef.current = null;
|
|
19762
20374
|
setHoveredNavContainerRect(null);
|
|
20375
|
+
siblingHintElRef.current = null;
|
|
20376
|
+
setSiblingHintRect(null);
|
|
20377
|
+
setSiblingHintRects([]);
|
|
20378
|
+
dismissImageHover();
|
|
20379
|
+
clearImageHover();
|
|
20380
|
+
setSectionGap(null);
|
|
19763
20381
|
return;
|
|
19764
20382
|
}
|
|
19765
20383
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -19771,7 +20389,11 @@ function OhhwellsBridge() {
|
|
|
19771
20389
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
19772
20390
|
const { clientX, clientY } = e.data;
|
|
19773
20391
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
19774
|
-
if (
|
|
20392
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
20393
|
+
dismissImageHover();
|
|
20394
|
+
clearImageHover();
|
|
20395
|
+
return;
|
|
20396
|
+
}
|
|
19775
20397
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
19776
20398
|
probeSectionGapAt(clientX, clientY);
|
|
19777
20399
|
probeImageAt(clientX, clientY);
|
|
@@ -20050,6 +20672,44 @@ function OhhwellsBridge() {
|
|
|
20050
20672
|
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20051
20673
|
}, 400));
|
|
20052
20674
|
};
|
|
20675
|
+
const reapCommittedAiSections = (excludeIds) => {
|
|
20676
|
+
const aiState = parseAiSectionsState(aiSectionsRef.current);
|
|
20677
|
+
if (aiState.sections.length === 0) return [];
|
|
20678
|
+
let orderEntries = [];
|
|
20679
|
+
try {
|
|
20680
|
+
const parsed = JSON.parse(editContentRef.current[SECTION_ORDER_KEY] ?? "[]");
|
|
20681
|
+
if (Array.isArray(parsed)) orderEntries = parsed;
|
|
20682
|
+
} catch {
|
|
20683
|
+
return [];
|
|
20684
|
+
}
|
|
20685
|
+
const removedIds = orderEntries.filter((entry) => entry && typeof entry.instanceId === "string" && entry.removed).map((entry) => entry.instanceId);
|
|
20686
|
+
if (removedIds.length === 0) return [];
|
|
20687
|
+
const result = reapRemovedAiSections(aiState, parseStyleStore(stylesRef.current), removedIds, excludeIds);
|
|
20688
|
+
if (!result.changed) return [];
|
|
20689
|
+
const nodes = [];
|
|
20690
|
+
aiSectionsRef.current = serializeAiSectionsState(result.state);
|
|
20691
|
+
nodes.push({ key: AI_SECTIONS_KEY, text: aiSectionsRef.current });
|
|
20692
|
+
const reaped = new Set(result.reapedIds);
|
|
20693
|
+
const nextOrderJson = JSON.stringify(orderEntries.filter((entry) => !reaped.has(entry.instanceId)));
|
|
20694
|
+
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: nextOrderJson };
|
|
20695
|
+
setAiSectionOrder(nextOrderJson, window.location.pathname);
|
|
20696
|
+
nodes.push({ key: SECTION_ORDER_KEY, text: nextOrderJson });
|
|
20697
|
+
if (result.store) {
|
|
20698
|
+
stylesRef.current = JSON.stringify(result.store);
|
|
20699
|
+
nodes.push({ key: STYLE_STORE_KEY, text: stylesRef.current });
|
|
20700
|
+
}
|
|
20701
|
+
const nextContent = { ...editContentRef.current };
|
|
20702
|
+
for (const key of Object.keys(nextContent)) {
|
|
20703
|
+
if (key.startsWith(AI_SLOT_KEY_PREFIX) && result.slotPrefixes.some((prefix) => key.startsWith(prefix)) && nextContent[key] !== "") {
|
|
20704
|
+
nextContent[key] = "";
|
|
20705
|
+
nodes.push({ key, text: "" });
|
|
20706
|
+
}
|
|
20707
|
+
}
|
|
20708
|
+
editContentRef.current = nextContent;
|
|
20709
|
+
applyAiSectionsToDom(result.state);
|
|
20710
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20711
|
+
return nodes;
|
|
20712
|
+
};
|
|
20053
20713
|
const handleHydrate = (e) => {
|
|
20054
20714
|
if (e.data?.type !== "ow:hydrate") return;
|
|
20055
20715
|
const content = e.data.content;
|
|
@@ -20068,6 +20728,7 @@ function OhhwellsBridge() {
|
|
|
20068
20728
|
}
|
|
20069
20729
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
20070
20730
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
20731
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
20071
20732
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
20072
20733
|
}
|
|
20073
20734
|
applyBrandChrome(content);
|
|
@@ -20079,11 +20740,11 @@ function OhhwellsBridge() {
|
|
|
20079
20740
|
continue;
|
|
20080
20741
|
}
|
|
20081
20742
|
if (key === AI_SECTIONS_KEY) continue;
|
|
20743
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
20744
|
+
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
20082
20745
|
if (key === BRAND_KIT_KEY) continue;
|
|
20083
20746
|
if (key === STYLE_STORE_KEY) continue;
|
|
20084
20747
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
20085
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
20086
|
-
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
20087
20748
|
if (applyVideoSettingNode(key, val)) continue;
|
|
20088
20749
|
if (applyCarouselNode(key, val)) continue;
|
|
20089
20750
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -20097,6 +20758,8 @@ function OhhwellsBridge() {
|
|
|
20097
20758
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
20098
20759
|
} else if (el.dataset.ohwEditable === "link") {
|
|
20099
20760
|
applyLinkHref(el, val);
|
|
20761
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
20762
|
+
applyMapQuery(el, val);
|
|
20100
20763
|
} else if (el.dataset.ohwEditable === "icon") {
|
|
20101
20764
|
applyIconMarkup(el, val);
|
|
20102
20765
|
} else if (isIconMarkupValue(val)) {
|
|
@@ -20118,8 +20781,16 @@ function OhhwellsBridge() {
|
|
|
20118
20781
|
reconcileFooterOrderFromContent(editContentRef.current);
|
|
20119
20782
|
syncNavigationDragCursorAttrs();
|
|
20120
20783
|
enforceLinkHrefs();
|
|
20784
|
+
const hydrateReapExclude = /* @__PURE__ */ new Set();
|
|
20785
|
+
const hydratePendingUndo = pendingDeleteUndoRef.current;
|
|
20786
|
+
if (hydratePendingUndo?.sectionInstanceId) hydrateReapExclude.add(hydratePendingUndo.sectionInstanceId);
|
|
20787
|
+
const reapNodes = reapCommittedAiSections(hydrateReapExclude);
|
|
20788
|
+
if (reapNodes.length > 0) postToParentRef.current({ type: "ow:change", nodes: reapNodes });
|
|
20121
20789
|
const hydratedHeight = document.body.scrollHeight;
|
|
20122
20790
|
if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
|
|
20791
|
+
if (parseAiSectionsState(aiSectionsRef.current).sections.length > 0) {
|
|
20792
|
+
postAiSectionsChanged();
|
|
20793
|
+
}
|
|
20123
20794
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
20124
20795
|
};
|
|
20125
20796
|
const handleUpdateLogoIdentity = (e) => {
|
|
@@ -20257,12 +20928,35 @@ function OhhwellsBridge() {
|
|
|
20257
20928
|
window.addEventListener("message", handleAiSetBrand);
|
|
20258
20929
|
const handleAiSetStyles = (e) => {
|
|
20259
20930
|
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20260
|
-
|
|
20931
|
+
let value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20261
20932
|
const previous = stylesRef.current;
|
|
20933
|
+
let previousSections;
|
|
20934
|
+
const store = parseStyleStore(value);
|
|
20935
|
+
if (store) {
|
|
20936
|
+
const folded = foldAlignIntoTrees(parseAiSectionsState(aiSectionsRef.current), store);
|
|
20937
|
+
if (folded.changed) {
|
|
20938
|
+
const nextSections = serializeAiSectionsState(folded.state);
|
|
20939
|
+
if (nextSections !== aiSectionsRef.current) {
|
|
20940
|
+
previousSections = aiSectionsRef.current;
|
|
20941
|
+
aiSectionsRef.current = nextSections;
|
|
20942
|
+
applyAiSectionsToDom(folded.state);
|
|
20943
|
+
postToParentRef.current({
|
|
20944
|
+
type: "ow:change",
|
|
20945
|
+
nodes: [{ key: AI_SECTIONS_KEY, text: nextSections }]
|
|
20946
|
+
});
|
|
20947
|
+
}
|
|
20948
|
+
value = JSON.stringify(folded.store);
|
|
20949
|
+
}
|
|
20950
|
+
}
|
|
20262
20951
|
stylesRef.current = value;
|
|
20263
20952
|
applyStylesToDom(parseStyleStore(value));
|
|
20264
20953
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20265
|
-
postToParentRef.current({
|
|
20954
|
+
postToParentRef.current({
|
|
20955
|
+
type: "ow:ai-styles-applied",
|
|
20956
|
+
previous,
|
|
20957
|
+
value,
|
|
20958
|
+
...previousSections !== void 0 ? { previousSections } : {}
|
|
20959
|
+
});
|
|
20266
20960
|
};
|
|
20267
20961
|
window.addEventListener("message", handleAiSetStyles);
|
|
20268
20962
|
const handleGetBrand = (e) => {
|
|
@@ -20279,8 +20973,11 @@ function OhhwellsBridge() {
|
|
|
20279
20973
|
if (!instanceId || !direction) return;
|
|
20280
20974
|
const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
|
|
20281
20975
|
if (!entries) return;
|
|
20282
|
-
const orderJson = JSON.stringify(
|
|
20976
|
+
const orderJson = JSON.stringify(
|
|
20977
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
20978
|
+
);
|
|
20283
20979
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20980
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
20284
20981
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20285
20982
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
20286
20983
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20326,8 +21023,11 @@ function OhhwellsBridge() {
|
|
|
20326
21023
|
const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
|
|
20327
21024
|
const entries = deleteSectionInstance(instanceId, window.location.pathname, currentEntries);
|
|
20328
21025
|
if (!entries) return;
|
|
20329
|
-
const orderJson = JSON.stringify(
|
|
21026
|
+
const orderJson = JSON.stringify(
|
|
21027
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
21028
|
+
);
|
|
20330
21029
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
21030
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20331
21031
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
20332
21032
|
aiSectionApiRef.current?.clear();
|
|
20333
21033
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20336,6 +21036,7 @@ function OhhwellsBridge() {
|
|
|
20336
21036
|
const actionId = newInstanceId();
|
|
20337
21037
|
pendingDeleteUndoRef.current = {
|
|
20338
21038
|
actionId,
|
|
21039
|
+
sectionInstanceId: instanceId,
|
|
20339
21040
|
restore: () => {
|
|
20340
21041
|
const restoredEntries = getPageSectionOrderEntries(
|
|
20341
21042
|
editContentRef.current[SECTION_ORDER_KEY],
|
|
@@ -20343,8 +21044,11 @@ function OhhwellsBridge() {
|
|
|
20343
21044
|
);
|
|
20344
21045
|
const restored = restoreSectionInstance(instanceId, window.location.pathname, restoredEntries);
|
|
20345
21046
|
if (!restored) return;
|
|
20346
|
-
const restoredJson = JSON.stringify(
|
|
21047
|
+
const restoredJson = JSON.stringify(
|
|
21048
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, restored)
|
|
21049
|
+
);
|
|
20347
21050
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
|
|
21051
|
+
setAiSectionOrder(restoredJson, window.location.pathname);
|
|
20348
21052
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
|
|
20349
21053
|
window.dispatchEvent(new Event("resize"));
|
|
20350
21054
|
const restoreHeight = document.body.scrollHeight;
|
|
@@ -20370,7 +21074,9 @@ function OhhwellsBridge() {
|
|
|
20370
21074
|
const result = duplicateSectionInstance(instanceId, newId, window.location.pathname, currentEntries);
|
|
20371
21075
|
if (!result) return;
|
|
20372
21076
|
const { entries, keyRekeys } = result;
|
|
20373
|
-
const orderJson = JSON.stringify(
|
|
21077
|
+
const orderJson = JSON.stringify(
|
|
21078
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
21079
|
+
);
|
|
20374
21080
|
const nodes = [{ key: SECTION_ORDER_KEY, text: orderJson }];
|
|
20375
21081
|
for (const { from, to } of keyRekeys) {
|
|
20376
21082
|
const inherited = editContentRef.current[from];
|
|
@@ -20380,6 +21086,7 @@ function OhhwellsBridge() {
|
|
|
20380
21086
|
...editContentRef.current,
|
|
20381
21087
|
...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
|
|
20382
21088
|
};
|
|
21089
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
20383
21090
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20384
21091
|
postToParentRef.current({ type: "ow:change", nodes });
|
|
20385
21092
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20398,6 +21105,12 @@ function OhhwellsBridge() {
|
|
|
20398
21105
|
closeLinkPopoverRef.current();
|
|
20399
21106
|
return;
|
|
20400
21107
|
}
|
|
21108
|
+
if (floatingPanelOpenRef.current) {
|
|
21109
|
+
setFloatingPanelRef.current(null);
|
|
21110
|
+
deselectRef.current();
|
|
21111
|
+
deactivateRef.current();
|
|
21112
|
+
return;
|
|
21113
|
+
}
|
|
20401
21114
|
deselectRef.current();
|
|
20402
21115
|
deactivateRef.current();
|
|
20403
21116
|
clearMediaSelectionRef.current();
|
|
@@ -20643,6 +21356,10 @@ function OhhwellsBridge() {
|
|
|
20643
21356
|
};
|
|
20644
21357
|
const handleSave = (e) => {
|
|
20645
21358
|
if (e.data?.type !== "ow:save") return;
|
|
21359
|
+
const pendingUndo = pendingDeleteUndoRef.current;
|
|
21360
|
+
const reapExclude = /* @__PURE__ */ new Set();
|
|
21361
|
+
if (pendingUndo?.sectionInstanceId) reapExclude.add(pendingUndo.sectionInstanceId);
|
|
21362
|
+
const reapNodes = reapCommittedAiSections(reapExclude);
|
|
20646
21363
|
const nodes = collectEditableNodes(editContentRef.current);
|
|
20647
21364
|
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
20648
21365
|
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
@@ -20662,6 +21379,11 @@ function OhhwellsBridge() {
|
|
|
20662
21379
|
const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
|
|
20663
21380
|
if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
|
|
20664
21381
|
});
|
|
21382
|
+
for (const reapNode of reapNodes) {
|
|
21383
|
+
if (reapNode.key.startsWith(AI_SLOT_KEY_PREFIX) && !nodes.some((n) => n.key === reapNode.key)) {
|
|
21384
|
+
nodes.push({ key: reapNode.key, type: "text", text: reapNode.text });
|
|
21385
|
+
}
|
|
21386
|
+
}
|
|
20665
21387
|
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
20666
21388
|
};
|
|
20667
21389
|
const handleInsertSection = (e) => {
|
|
@@ -20672,8 +21394,12 @@ function OhhwellsBridge() {
|
|
|
20672
21394
|
if (inserted) {
|
|
20673
21395
|
const tracker = getSectionsTracker();
|
|
20674
21396
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
20675
|
-
const
|
|
20676
|
-
|
|
21397
|
+
const reportHeight = () => {
|
|
21398
|
+
const h = document.body.scrollHeight;
|
|
21399
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
21400
|
+
};
|
|
21401
|
+
reportHeight();
|
|
21402
|
+
setTimeout(reportHeight, 500);
|
|
20677
21403
|
}
|
|
20678
21404
|
};
|
|
20679
21405
|
const handleSwitchSchedule = (e) => {
|
|
@@ -21077,10 +21803,10 @@ function OhhwellsBridge() {
|
|
|
21077
21803
|
window.removeEventListener("message", handleDeleteSection);
|
|
21078
21804
|
window.removeEventListener("message", handleDuplicateSection);
|
|
21079
21805
|
window.removeEventListener("message", handleDeactivate);
|
|
21080
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
21081
21806
|
window.removeEventListener("message", handleToastAction);
|
|
21082
21807
|
window.removeEventListener("message", handleFormCount);
|
|
21083
21808
|
window.removeEventListener("message", handleUiEscape);
|
|
21809
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
21084
21810
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
21085
21811
|
autoSaveTimers.current.clear();
|
|
21086
21812
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -21283,7 +22009,7 @@ function OhhwellsBridge() {
|
|
|
21283
22009
|
postToParent2({
|
|
21284
22010
|
type: "ow:ready",
|
|
21285
22011
|
version: "1",
|
|
21286
|
-
bridgeVersion: "0.1.
|
|
22012
|
+
bridgeVersion: "0.1.92",
|
|
21287
22013
|
path: pathname,
|
|
21288
22014
|
nodes: collectEditableNodes(editContentRef.current),
|
|
21289
22015
|
sections
|
|
@@ -22202,6 +22928,59 @@ function OhhwellsBridge() {
|
|
|
22202
22928
|
) : null
|
|
22203
22929
|
] });
|
|
22204
22930
|
}
|
|
22931
|
+
|
|
22932
|
+
// src/ui/EmptySection.tsx
|
|
22933
|
+
import Link3 from "next/link";
|
|
22934
|
+
import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
22935
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
22936
|
+
return /* @__PURE__ */ jsxs21(Fragment9, { children: [
|
|
22937
|
+
/* @__PURE__ */ jsx34(
|
|
22938
|
+
"p",
|
|
22939
|
+
{
|
|
22940
|
+
style: {
|
|
22941
|
+
fontFamily: "var(--brand-font-body)",
|
|
22942
|
+
fontSize: "0.75rem",
|
|
22943
|
+
fontWeight: 500,
|
|
22944
|
+
letterSpacing: "0.15em",
|
|
22945
|
+
textTransform: "uppercase",
|
|
22946
|
+
color: "var(--brand-accent)",
|
|
22947
|
+
marginBottom: "1.5rem"
|
|
22948
|
+
},
|
|
22949
|
+
children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
22950
|
+
}
|
|
22951
|
+
),
|
|
22952
|
+
/* @__PURE__ */ jsx34(
|
|
22953
|
+
"h1",
|
|
22954
|
+
{
|
|
22955
|
+
style: {
|
|
22956
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22957
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22958
|
+
lineHeight: 1.1,
|
|
22959
|
+
letterSpacing: "-0.025em",
|
|
22960
|
+
color: "var(--brand-text)",
|
|
22961
|
+
marginBottom: "1rem"
|
|
22962
|
+
},
|
|
22963
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22964
|
+
children: title
|
|
22965
|
+
}
|
|
22966
|
+
),
|
|
22967
|
+
/* @__PURE__ */ jsx34(
|
|
22968
|
+
"p",
|
|
22969
|
+
{
|
|
22970
|
+
style: {
|
|
22971
|
+
fontFamily: "var(--brand-font-body)",
|
|
22972
|
+
fontSize: "1rem",
|
|
22973
|
+
lineHeight: 1.7,
|
|
22974
|
+
fontWeight: 300,
|
|
22975
|
+
color: "var(--brand-text-muted)",
|
|
22976
|
+
maxWidth: "340px"
|
|
22977
|
+
},
|
|
22978
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22979
|
+
children: "This page doesn't have any content yet."
|
|
22980
|
+
}
|
|
22981
|
+
)
|
|
22982
|
+
] });
|
|
22983
|
+
}
|
|
22205
22984
|
export {
|
|
22206
22985
|
AI_DEFAULT_BRAND,
|
|
22207
22986
|
AI_TREE_SCHEMA_VERSIONS,
|
|
@@ -22218,6 +22997,7 @@ export {
|
|
|
22218
22997
|
DropdownMenuItem,
|
|
22219
22998
|
DropdownMenuSeparator,
|
|
22220
22999
|
DropdownMenuTrigger,
|
|
23000
|
+
EmptySection,
|
|
22221
23001
|
ItemActionToolbar,
|
|
22222
23002
|
ItemInteractionLayer,
|
|
22223
23003
|
LinkEditorPanel,
|