@ohhwells/bridge 0.1.91 → 0.1.92-next.267
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 +1155 -397
- 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 +1154 -397
- 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,7 +2565,7 @@ 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"]);
|
|
1826
2571
|
function readRootVar(name) {
|
|
@@ -1844,13 +2589,13 @@ function deriveBrandOverride() {
|
|
|
1844
2589
|
};
|
|
1845
2590
|
}
|
|
1846
2591
|
function deriveTemplateBrand() {
|
|
1847
|
-
const
|
|
1848
|
-
const
|
|
1849
|
-
const light = readRootVar("--color-light");
|
|
2592
|
+
const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
|
|
2593
|
+
const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
|
|
2594
|
+
const light = readRootVar("--brand-background") || readRootVar("--color-light");
|
|
1850
2595
|
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");
|
|
2596
|
+
const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
|
|
2597
|
+
const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
|
|
2598
|
+
const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
|
|
1854
2599
|
return {
|
|
1855
2600
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1856
2601
|
fonts: {
|
|
@@ -1859,12 +2604,32 @@ function deriveTemplateBrand() {
|
|
|
1859
2604
|
}
|
|
1860
2605
|
};
|
|
1861
2606
|
}
|
|
1862
|
-
function
|
|
2607
|
+
function deriveTemplateButtonStyle() {
|
|
1863
2608
|
if (typeof document === "undefined") return null;
|
|
1864
|
-
const btn = document.
|
|
2609
|
+
const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
|
|
2610
|
+
(el) => !el.closest(`[${CONTAINER_ATTR}]`)
|
|
2611
|
+
);
|
|
1865
2612
|
if (!btn) return null;
|
|
1866
|
-
const
|
|
1867
|
-
|
|
2613
|
+
const cs = getComputedStyle(btn);
|
|
2614
|
+
const corners = [
|
|
2615
|
+
cs.borderTopLeftRadius,
|
|
2616
|
+
cs.borderTopRightRadius,
|
|
2617
|
+
cs.borderBottomRightRadius,
|
|
2618
|
+
cs.borderBottomLeftRadius
|
|
2619
|
+
].map((v) => v || "0px");
|
|
2620
|
+
const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
|
|
2621
|
+
const px = (v) => parseFloat(v) || 0;
|
|
2622
|
+
const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom));
|
|
2623
|
+
const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
|
|
2624
|
+
return {
|
|
2625
|
+
radius: radius || "10px",
|
|
2626
|
+
padding: `${padY}px ${padX}px`,
|
|
2627
|
+
fontFamily: cs.fontFamily || "",
|
|
2628
|
+
fontSize: cs.fontSize || "",
|
|
2629
|
+
fontWeight: cs.fontWeight || "",
|
|
2630
|
+
letterSpacing: cs.letterSpacing || "",
|
|
2631
|
+
textTransform: cs.textTransform || ""
|
|
2632
|
+
};
|
|
1868
2633
|
}
|
|
1869
2634
|
var mounted = /* @__PURE__ */ new Map();
|
|
1870
2635
|
function findTemplateSection(id) {
|
|
@@ -1915,18 +2680,18 @@ function placeContainer(container, entry) {
|
|
|
1915
2680
|
}
|
|
1916
2681
|
function syncRemovedSections(state) {
|
|
1917
2682
|
const removed = new Set(state.removed ?? []);
|
|
1918
|
-
for (const el of document.querySelectorAll(`[${
|
|
2683
|
+
for (const el of document.querySelectorAll(`[${REMOVED_ATTR2}]`)) {
|
|
1919
2684
|
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
1920
2685
|
if (!removed.has(id)) {
|
|
1921
2686
|
el.style.removeProperty("display");
|
|
1922
|
-
el.removeAttribute(
|
|
2687
|
+
el.removeAttribute(REMOVED_ATTR2);
|
|
1923
2688
|
}
|
|
1924
2689
|
}
|
|
1925
2690
|
for (const id of removed) {
|
|
1926
2691
|
const section = findTemplateSection(id);
|
|
1927
2692
|
if (section && !section.hasAttribute(REPLACED_ATTR)) {
|
|
1928
2693
|
section.style.display = "none";
|
|
1929
|
-
section.setAttribute(
|
|
2694
|
+
section.setAttribute(REMOVED_ATTR2, "");
|
|
1930
2695
|
}
|
|
1931
2696
|
}
|
|
1932
2697
|
}
|
|
@@ -1943,7 +2708,7 @@ function syncTemplateHidden(state, pageHasSections) {
|
|
|
1943
2708
|
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
1944
2709
|
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
|
|
1945
2710
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
1946
|
-
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(
|
|
2711
|
+
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR2)) continue;
|
|
1947
2712
|
el.style.display = "none";
|
|
1948
2713
|
el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
|
|
1949
2714
|
}
|
|
@@ -1967,18 +2732,23 @@ function syncReplacedOriginals(state) {
|
|
|
1967
2732
|
}
|
|
1968
2733
|
}
|
|
1969
2734
|
var sectionOrderIndex = /* @__PURE__ */ new Map();
|
|
2735
|
+
var removedSectionIds = /* @__PURE__ */ new Set();
|
|
1970
2736
|
function setAiSectionOrder(raw, currentPath) {
|
|
1971
2737
|
const next = /* @__PURE__ */ new Map();
|
|
2738
|
+
const removed = /* @__PURE__ */ new Set();
|
|
1972
2739
|
if (raw) {
|
|
1973
2740
|
try {
|
|
1974
2741
|
const entries = JSON.parse(raw);
|
|
1975
2742
|
for (const entry of entries) {
|
|
1976
|
-
if (
|
|
2743
|
+
if (entry.pagePath && entry.pagePath !== currentPath) continue;
|
|
2744
|
+
next.set(entry.instanceId, entry.order);
|
|
2745
|
+
if (entry.removed) removed.add(entry.instanceId);
|
|
1977
2746
|
}
|
|
1978
2747
|
} catch {
|
|
1979
2748
|
}
|
|
1980
2749
|
}
|
|
1981
2750
|
sectionOrderIndex = next;
|
|
2751
|
+
removedSectionIds = removed;
|
|
1982
2752
|
}
|
|
1983
2753
|
function applyExplicitOrder(entries) {
|
|
1984
2754
|
if (sectionOrderIndex.size === 0) return entries;
|
|
@@ -2014,11 +2784,23 @@ function orderByChain(sections) {
|
|
|
2014
2784
|
for (const root of roots) visit(root);
|
|
2015
2785
|
return out.length === sections.length ? out : sections;
|
|
2016
2786
|
}
|
|
2787
|
+
function syncSoftRemovedGenerated() {
|
|
2788
|
+
for (const [id, section] of mounted) {
|
|
2789
|
+
const el = section.container;
|
|
2790
|
+
if (removedSectionIds.has(id)) {
|
|
2791
|
+
el.style.display = "none";
|
|
2792
|
+
el.setAttribute(REMOVED_ATTR, "");
|
|
2793
|
+
} else if (el.hasAttribute(REMOVED_ATTR)) {
|
|
2794
|
+
el.style.removeProperty("display");
|
|
2795
|
+
el.removeAttribute(REMOVED_ATTR);
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2017
2799
|
function applyAiSectionsToDom(state, options) {
|
|
2018
2800
|
if (typeof document === "undefined") return;
|
|
2019
2801
|
const brandOverride = deriveBrandOverride();
|
|
2020
2802
|
const templateBrand = deriveTemplateBrand();
|
|
2021
|
-
const
|
|
2803
|
+
const templateButtonStyle = deriveTemplateButtonStyle();
|
|
2022
2804
|
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2023
2805
|
const pagePath = window.location.pathname;
|
|
2024
2806
|
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
@@ -2058,7 +2840,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2058
2840
|
{
|
|
2059
2841
|
tree: entry.tree,
|
|
2060
2842
|
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2061
|
-
|
|
2843
|
+
buttonStyle: templateButtonStyle,
|
|
2062
2844
|
resolveMedia,
|
|
2063
2845
|
editKeyPrefix: `ai.${entry.id}`
|
|
2064
2846
|
}
|
|
@@ -2081,6 +2863,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2081
2863
|
syncReplacedOriginals(state);
|
|
2082
2864
|
syncRemovedSections(state);
|
|
2083
2865
|
syncTemplateHidden(state, pageSections.length > 0);
|
|
2866
|
+
syncSoftRemovedGenerated();
|
|
2084
2867
|
}
|
|
2085
2868
|
|
|
2086
2869
|
// src/useLinkHrefGuardian.ts
|
|
@@ -7810,6 +8593,7 @@ function MediaOverlay({
|
|
|
7810
8593
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
7811
8594
|
);
|
|
7812
8595
|
}, [isVideo]);
|
|
8596
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
7813
8597
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
7814
8598
|
const box = {
|
|
7815
8599
|
position: "fixed",
|
|
@@ -7939,17 +8723,17 @@ function MediaOverlay({
|
|
|
7939
8723
|
},
|
|
7940
8724
|
children: [
|
|
7941
8725
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7942
|
-
|
|
8726
|
+
replaceLabel
|
|
7943
8727
|
]
|
|
7944
8728
|
}
|
|
7945
8729
|
),
|
|
7946
|
-
replaceMode
|
|
8730
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
|
|
7947
8731
|
Button,
|
|
7948
8732
|
{
|
|
7949
8733
|
"data-ohw-media-overlay": "",
|
|
7950
8734
|
variant: "outline",
|
|
7951
8735
|
size: "sm",
|
|
7952
|
-
"aria-label":
|
|
8736
|
+
"aria-label": replaceLabel,
|
|
7953
8737
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
7954
8738
|
style: {
|
|
7955
8739
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -7972,7 +8756,7 @@ function MediaOverlay({
|
|
|
7972
8756
|
},
|
|
7973
8757
|
children: [
|
|
7974
8758
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7975
|
-
replaceMode === "full" ?
|
|
8759
|
+
replaceMode === "full" ? replaceLabel : null
|
|
7976
8760
|
]
|
|
7977
8761
|
}
|
|
7978
8762
|
)
|
|
@@ -8025,235 +8809,23 @@ function CarouselOverlay({
|
|
|
8025
8809
|
onMouseDown: (e) => e.preventDefault(),
|
|
8026
8810
|
onClick: (e) => {
|
|
8027
8811
|
e.stopPropagation();
|
|
8028
|
-
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, "");
|
|
8812
|
+
onEdit(hover.key);
|
|
8813
|
+
},
|
|
8814
|
+
children: [
|
|
8815
|
+
/* @__PURE__ */ jsx16(GalleryHorizontal, { size: 14 }),
|
|
8816
|
+
"Edit gallery"
|
|
8817
|
+
]
|
|
8818
|
+
}
|
|
8819
|
+
)
|
|
8133
8820
|
}
|
|
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
8821
|
);
|
|
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
8822
|
}
|
|
8250
8823
|
|
|
8251
8824
|
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
8825
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
|
|
8826
|
+
import { Check, X } from "lucide-react";
|
|
8252
8827
|
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
|
-
}
|
|
8828
|
+
var findSectionElement = findByInstanceId;
|
|
8257
8829
|
function readRect(instanceId) {
|
|
8258
8830
|
const el = findSectionElement(instanceId);
|
|
8259
8831
|
if (!el) return null;
|
|
@@ -8293,7 +8865,7 @@ function useLiveSectionRect(sectionId) {
|
|
|
8293
8865
|
}
|
|
8294
8866
|
function computeSectionBoundaryFlags(instanceId) {
|
|
8295
8867
|
const topLevel = topLevelSections();
|
|
8296
|
-
const index = topLevel.findIndex((el) => (el
|
|
8868
|
+
const index = topLevel.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
8297
8869
|
if (index === -1) return { isFirst: true, isLast: true };
|
|
8298
8870
|
return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
|
|
8299
8871
|
}
|
|
@@ -8377,18 +8949,20 @@ function AiSectionOverlay({
|
|
|
8377
8949
|
selectedIdRef.current = selectedId;
|
|
8378
8950
|
const report = useCallback2(
|
|
8379
8951
|
(el) => {
|
|
8952
|
+
const labelSrc = el ? sectionElementOf(el) : null;
|
|
8380
8953
|
postToParent2({
|
|
8381
8954
|
type: "ow:section-selected",
|
|
8382
|
-
sectionId: el ? el
|
|
8383
|
-
sectionLabel:
|
|
8955
|
+
sectionId: el ? instanceIdOf(el) || null : null,
|
|
8956
|
+
sectionLabel: labelSrc ? labelSrc.dataset.ohwSectionLabel ?? titleCaseSectionId(labelSrc.dataset.ohwSection ?? "") : null
|
|
8384
8957
|
});
|
|
8385
8958
|
},
|
|
8386
8959
|
[postToParent2]
|
|
8387
8960
|
);
|
|
8388
8961
|
const selectFromElement = useCallback2(
|
|
8389
8962
|
(el, options) => {
|
|
8390
|
-
const
|
|
8391
|
-
const
|
|
8963
|
+
const inner = el?.closest("[data-ohw-section]") ?? null;
|
|
8964
|
+
const sectionEl = inner ? movableUnit(inner) : null;
|
|
8965
|
+
const id = sectionEl ? instanceIdOf(sectionEl) || null : null;
|
|
8392
8966
|
if (id === selectedIdRef.current) return;
|
|
8393
8967
|
setSelectedId(id);
|
|
8394
8968
|
if (options?.report !== false) report(sectionEl);
|
|
@@ -8454,7 +9028,8 @@ function AiSectionOverlay({
|
|
|
8454
9028
|
return;
|
|
8455
9029
|
}
|
|
8456
9030
|
const sec = t.closest("[data-ohw-section]");
|
|
8457
|
-
|
|
9031
|
+
const unit = sec ? movableUnit(sec) : null;
|
|
9032
|
+
setHoveredId(unit ? instanceIdOf(unit) || null : null);
|
|
8458
9033
|
};
|
|
8459
9034
|
const onLeave = () => setHoveredId(null);
|
|
8460
9035
|
document.addEventListener("mousemove", onMove, { passive: true });
|
|
@@ -12965,6 +13540,7 @@ function readLogoSizeState(content, placement) {
|
|
|
12965
13540
|
function getLogoElement(el) {
|
|
12966
13541
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
12967
13542
|
if (marked) return marked;
|
|
13543
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
12968
13544
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
12969
13545
|
if (!root) return null;
|
|
12970
13546
|
const anchor = el.closest("a");
|
|
@@ -14037,15 +14613,17 @@ function useSectionDrag({
|
|
|
14037
14613
|
clearSectionDragVisuals();
|
|
14038
14614
|
return;
|
|
14039
14615
|
}
|
|
14040
|
-
const orderJson = JSON.stringify(
|
|
14616
|
+
const orderJson = JSON.stringify(
|
|
14617
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
14618
|
+
);
|
|
14041
14619
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
14042
14620
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
14043
14621
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
14044
|
-
applyPersistedOrder(
|
|
14622
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
14045
14623
|
clearSectionDragVisuals();
|
|
14046
14624
|
requestAnimationFrame(() => {
|
|
14047
14625
|
if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
|
|
14048
|
-
applyPersistedOrder(
|
|
14626
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
14049
14627
|
}
|
|
14050
14628
|
requestAnimationFrame(() => {
|
|
14051
14629
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -14078,8 +14656,9 @@ function useSectionDrag({
|
|
|
14078
14656
|
const target = e.target;
|
|
14079
14657
|
if (!(target instanceof HTMLElement)) return;
|
|
14080
14658
|
if (target.closest(SECTION_DRAG_EXCLUDED_SELECTOR)) return;
|
|
14081
|
-
const
|
|
14082
|
-
if (!
|
|
14659
|
+
const inner = target.closest("[data-ohw-section]");
|
|
14660
|
+
if (!inner || isChromeSection(inner) || inner.dataset.ohwSection === "footer") return;
|
|
14661
|
+
const sectionEl = movableUnit(inner);
|
|
14083
14662
|
if (!topLevelSections().includes(sectionEl)) return;
|
|
14084
14663
|
startSectionPressDrag(sectionEl, e.clientX, e.clientY, e.pointerId);
|
|
14085
14664
|
};
|
|
@@ -14346,6 +14925,9 @@ function collectEditableNodes(extraContent, root = document) {
|
|
|
14346
14925
|
if (el.dataset.ohwEditable === "link") {
|
|
14347
14926
|
return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref3(el) };
|
|
14348
14927
|
}
|
|
14928
|
+
if (el.dataset.ohwEditable === "map") {
|
|
14929
|
+
return { key: el.dataset.ohwKey ?? "", type: "map", text: el.dataset.ohwMapQuery ?? "" };
|
|
14930
|
+
}
|
|
14349
14931
|
return {
|
|
14350
14932
|
key: el.dataset.ohwKey ?? "",
|
|
14351
14933
|
type: el.dataset.ohwEditable ?? "text",
|
|
@@ -14908,21 +15490,10 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
14908
15490
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
14909
15491
|
};
|
|
14910
15492
|
}
|
|
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;
|
|
15493
|
+
function resolveEntryAnchor(entry) {
|
|
15494
|
+
if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
|
|
15495
|
+
const parsed = parseSchedulingInsertAfter(entry.insertAfter);
|
|
15496
|
+
return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
|
|
14926
15497
|
}
|
|
14927
15498
|
function schedulingMountDepth(insertAfter) {
|
|
14928
15499
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -14939,8 +15510,7 @@ function getPageSchedulingEntries(raw) {
|
|
|
14939
15510
|
}
|
|
14940
15511
|
}
|
|
14941
15512
|
function isSchedulingWidgetMissing(entry) {
|
|
14942
|
-
|
|
14943
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
15513
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
|
|
14944
15514
|
}
|
|
14945
15515
|
function hasMissingSchedulingWidgets(entries) {
|
|
14946
15516
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -14978,18 +15548,18 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
|
|
|
14978
15548
|
} catch {
|
|
14979
15549
|
}
|
|
14980
15550
|
}
|
|
14981
|
-
function mountSchedulingWidget(
|
|
14982
|
-
const
|
|
14983
|
-
const sectionId = schedulingSectionId(
|
|
15551
|
+
function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
|
|
15552
|
+
const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
|
|
15553
|
+
const sectionId = schedulingSectionId(widgetId);
|
|
14984
15554
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
14985
|
-
const
|
|
14986
|
-
if (!
|
|
15555
|
+
const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
|
|
15556
|
+
if (!anchorEl) return false;
|
|
15557
|
+
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14987
15558
|
const container = document.createElement("div");
|
|
14988
15559
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
14989
|
-
container.dataset.ohwSection = sectionId;
|
|
14990
15560
|
container.dataset.ohwInstance = sectionId;
|
|
14991
|
-
if (
|
|
14992
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
15561
|
+
if (beforeId) {
|
|
15562
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
|
|
14993
15563
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
14994
15564
|
if (!beforePoint) return false;
|
|
14995
15565
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -15000,20 +15570,26 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
15000
15570
|
}
|
|
15001
15571
|
tail.insertAdjacentElement("afterend", container);
|
|
15002
15572
|
}
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
|
|
15573
|
+
try {
|
|
15574
|
+
const root = createRoot2(container);
|
|
15575
|
+
schedulingRoots.set(container, root);
|
|
15576
|
+
flushSync2(() => {
|
|
15577
|
+
root.render(
|
|
15578
|
+
/* @__PURE__ */ jsx33(
|
|
15579
|
+
SchedulingWidget,
|
|
15580
|
+
{
|
|
15581
|
+
notifyOnConnect,
|
|
15582
|
+
initialScheduleId: scheduleId,
|
|
15583
|
+
insertAfter: widgetId
|
|
15584
|
+
}
|
|
15585
|
+
)
|
|
15586
|
+
);
|
|
15587
|
+
});
|
|
15588
|
+
} catch (err) {
|
|
15589
|
+
console.error("[ow:scheduling] render threw", err);
|
|
15590
|
+
container.remove();
|
|
15591
|
+
return false;
|
|
15592
|
+
}
|
|
15017
15593
|
const tracker = getSectionsTracker();
|
|
15018
15594
|
let sections = [];
|
|
15019
15595
|
try {
|
|
@@ -15021,10 +15597,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
15021
15597
|
} catch {
|
|
15022
15598
|
}
|
|
15023
15599
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
15024
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
15600
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
|
|
15025
15601
|
sections.push({
|
|
15026
15602
|
type: "scheduling",
|
|
15027
|
-
insertAfter:
|
|
15603
|
+
insertAfter: widgetId,
|
|
15604
|
+
anchorId,
|
|
15605
|
+
beforeId: beforeId ?? null,
|
|
15028
15606
|
pagePath: window.location.pathname,
|
|
15029
15607
|
...scheduleId ? { scheduleId } : {}
|
|
15030
15608
|
});
|
|
@@ -15038,7 +15616,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
15038
15616
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
15039
15617
|
const entry = pending[i];
|
|
15040
15618
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
15041
|
-
|
|
15619
|
+
const { anchorId, beforeId } = resolveEntryAnchor(entry);
|
|
15620
|
+
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
15042
15621
|
pending.splice(i, 1);
|
|
15043
15622
|
}
|
|
15044
15623
|
}
|
|
@@ -15130,7 +15709,7 @@ var EDITOR_CHROME_SELECTOR = '[data-ohw-toolbar], [data-ohw-form-toolbar], [data
|
|
|
15130
15709
|
function isOverEditorChrome(x, y) {
|
|
15131
15710
|
return document.elementsFromPoint(x, y).some((el) => el instanceof HTMLElement && el.matches(EDITOR_CHROME_SELECTOR));
|
|
15132
15711
|
}
|
|
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"])';
|
|
15712
|
+
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
15713
|
function getVideoEl2(el) {
|
|
15135
15714
|
return el instanceof HTMLVideoElement ? el : el.querySelector("video");
|
|
15136
15715
|
}
|
|
@@ -15186,6 +15765,12 @@ function applyVideoSettingNode(key, val) {
|
|
|
15186
15765
|
});
|
|
15187
15766
|
return true;
|
|
15188
15767
|
}
|
|
15768
|
+
function applyMapQuery(el, val) {
|
|
15769
|
+
if (!(el instanceof HTMLIFrameElement)) return;
|
|
15770
|
+
const nextSrc = `https://www.google.com/maps?q=${encodeURIComponent(val)}&output=embed`;
|
|
15771
|
+
if (el.src !== nextSrc) el.src = nextSrc;
|
|
15772
|
+
el.setAttribute("data-ohw-map-query", val);
|
|
15773
|
+
}
|
|
15189
15774
|
function applyLinkByKey(key, val) {
|
|
15190
15775
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
15191
15776
|
if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
|
|
@@ -15196,6 +15781,11 @@ function applyLinkByKey(key, val) {
|
|
|
15196
15781
|
hrefAnchors.forEach((el) => applyLinkHref(el, val));
|
|
15197
15782
|
}
|
|
15198
15783
|
}
|
|
15784
|
+
function isInsideLinkEditor(target) {
|
|
15785
|
+
return Boolean(
|
|
15786
|
+
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"]')
|
|
15787
|
+
);
|
|
15788
|
+
}
|
|
15199
15789
|
function isInsideFloatingPanel(target) {
|
|
15200
15790
|
return Boolean(target.closest("[data-ohw-floating-panel]"));
|
|
15201
15791
|
}
|
|
@@ -15203,11 +15793,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
|
|
|
15203
15793
|
const el = document.elementFromPoint(clientX, clientY);
|
|
15204
15794
|
return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
|
|
15205
15795
|
}
|
|
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
15796
|
function getHrefKeyFromElement(el) {
|
|
15212
15797
|
if (!el) return null;
|
|
15213
15798
|
const anchor = el.closest("[data-ohw-href-key]");
|
|
@@ -15466,7 +16051,7 @@ function getNavigationSelectionParent(el) {
|
|
|
15466
16051
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
15467
16052
|
return getFooterLinksContainer();
|
|
15468
16053
|
}
|
|
15469
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
16054
|
+
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
16055
|
return getNavigationRoot(el);
|
|
15471
16056
|
}
|
|
15472
16057
|
return null;
|
|
@@ -15681,7 +16266,6 @@ var ICONS = {
|
|
|
15681
16266
|
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
16267
|
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
16268
|
};
|
|
15684
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
15685
16269
|
var SELECTION_CHROME_GAP2 = 4;
|
|
15686
16270
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
15687
16271
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -16061,6 +16645,7 @@ function StateToggle({
|
|
|
16061
16645
|
);
|
|
16062
16646
|
}
|
|
16063
16647
|
var contentCache = /* @__PURE__ */ new Map();
|
|
16648
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
16064
16649
|
var brandingCache = /* @__PURE__ */ new Map();
|
|
16065
16650
|
var OHW_LOADER_STYLE = {
|
|
16066
16651
|
position: "fixed",
|
|
@@ -16590,13 +17175,6 @@ function OhhwellsBridge() {
|
|
|
16590
17175
|
const [isItemDragging, setIsItemDragging] = useState13(false);
|
|
16591
17176
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
|
|
16592
17177
|
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
17178
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
|
|
16601
17179
|
const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
|
|
16602
17180
|
const footerDragRef = useRef10(null);
|
|
@@ -16614,6 +17192,13 @@ function OhhwellsBridge() {
|
|
|
16614
17192
|
const brandKitRef = useRef10("");
|
|
16615
17193
|
const stylesRef = useRef10("");
|
|
16616
17194
|
const pendingDeleteUndoRef = useRef10(null);
|
|
17195
|
+
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
17196
|
+
const floatingPanelOpenRef = useRef10(false);
|
|
17197
|
+
const setFloatingPanelRef = useRef10(setFloatingPanel);
|
|
17198
|
+
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
17199
|
+
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
17200
|
+
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
17201
|
+
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
16617
17202
|
const [sitePages, setSitePages] = useState13([]);
|
|
16618
17203
|
const [sectionsByPath, setSectionsByPath] = useState13({});
|
|
16619
17204
|
const sectionsPrefetchGenRef = useRef10(0);
|
|
@@ -16622,7 +17207,18 @@ function OhhwellsBridge() {
|
|
|
16622
17207
|
const linkPopoverOpenRef = useRef10(false);
|
|
16623
17208
|
const linkPopoverGraceUntilRef = useRef10(0);
|
|
16624
17209
|
setLinkPopoverRef.current = setLinkPopover;
|
|
17210
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
16625
17211
|
linkPopoverSessionRef.current = linkPopover;
|
|
17212
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
17213
|
+
useEffect13(() => {
|
|
17214
|
+
const syncViewport = () => {
|
|
17215
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
17216
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
17217
|
+
};
|
|
17218
|
+
syncViewport();
|
|
17219
|
+
window.addEventListener("resize", syncViewport);
|
|
17220
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
17221
|
+
}, []);
|
|
16626
17222
|
const {
|
|
16627
17223
|
navDragRef,
|
|
16628
17224
|
navDropSlots,
|
|
@@ -17947,6 +18543,7 @@ function OhhwellsBridge() {
|
|
|
17947
18543
|
}
|
|
17948
18544
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17949
18545
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
18546
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
17950
18547
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17951
18548
|
}
|
|
17952
18549
|
applyBrandChrome(content);
|
|
@@ -17954,11 +18551,11 @@ function OhhwellsBridge() {
|
|
|
17954
18551
|
for (const [key, val] of Object.entries(content)) {
|
|
17955
18552
|
if (key === "__ohw_sections") continue;
|
|
17956
18553
|
if (key === AI_SECTIONS_KEY) continue;
|
|
18554
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18555
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17957
18556
|
if (key === BRAND_KIT_KEY) continue;
|
|
17958
18557
|
if (key === STYLE_STORE_KEY) continue;
|
|
17959
18558
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17960
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17961
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17962
18559
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17963
18560
|
if (applyCarouselNode(key, val)) continue;
|
|
17964
18561
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17986,6 +18583,8 @@ function OhhwellsBridge() {
|
|
|
17986
18583
|
}
|
|
17987
18584
|
} else if (el.dataset.ohwEditable === "link") {
|
|
17988
18585
|
applyLinkHref(el, val);
|
|
18586
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
18587
|
+
applyMapQuery(el, val);
|
|
17989
18588
|
} else if (el.dataset.ohwEditable === "icon") {
|
|
17990
18589
|
applyIconMarkup(el, val);
|
|
17991
18590
|
} else if (el.dataset.ohwEditable === "form") {
|
|
@@ -18024,7 +18623,9 @@ function OhhwellsBridge() {
|
|
|
18024
18623
|
let cancelled = false;
|
|
18025
18624
|
setFetchState("loading");
|
|
18026
18625
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18027
|
-
|
|
18626
|
+
const initialPath = pathname;
|
|
18627
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
18628
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18028
18629
|
if (cancelled) return;
|
|
18029
18630
|
const content = data?.content ?? {};
|
|
18030
18631
|
const branding = Boolean(data?.showBranding);
|
|
@@ -18143,10 +18744,10 @@ function OhhwellsBridge() {
|
|
|
18143
18744
|
const applyFromCache = () => {
|
|
18144
18745
|
const content = contentCache.get(subdomain);
|
|
18145
18746
|
if (!content) return;
|
|
18146
|
-
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
18147
|
-
initSectionInstancesFromContent(content, window.location.pathname);
|
|
18148
18747
|
observer?.disconnect();
|
|
18149
18748
|
try {
|
|
18749
|
+
retryMissingSchedulingMounts(getPageSchedulingEntries(content["__ohw_sections"]), false);
|
|
18750
|
+
initSectionInstancesFromContent(content, window.location.pathname);
|
|
18150
18751
|
applyBrandChrome(content);
|
|
18151
18752
|
if (typeof content[BRAND_KIT_KEY] === "string") {
|
|
18152
18753
|
applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
|
|
@@ -18158,16 +18759,17 @@ function OhhwellsBridge() {
|
|
|
18158
18759
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18159
18760
|
}
|
|
18160
18761
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18762
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
18161
18763
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18162
18764
|
}
|
|
18163
18765
|
for (const [key, val] of Object.entries(content)) {
|
|
18164
18766
|
if (key === "__ohw_sections") continue;
|
|
18165
18767
|
if (key === AI_SECTIONS_KEY) continue;
|
|
18768
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18769
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18166
18770
|
if (key === BRAND_KIT_KEY) continue;
|
|
18167
18771
|
if (key === STYLE_STORE_KEY) continue;
|
|
18168
18772
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
18169
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18170
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18171
18773
|
if (applyVideoSettingNode(key, val)) continue;
|
|
18172
18774
|
if (applyCarouselNode(key, val)) continue;
|
|
18173
18775
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -18182,6 +18784,8 @@ function OhhwellsBridge() {
|
|
|
18182
18784
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
18183
18785
|
} else if (el.dataset.ohwEditable === "link") {
|
|
18184
18786
|
applyLinkHref(el, val);
|
|
18787
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
18788
|
+
applyMapQuery(el, val);
|
|
18185
18789
|
} else if (el.dataset.ohwEditable === "form") {
|
|
18186
18790
|
} else if (isIconMarkupValue(val)) {
|
|
18187
18791
|
} else if (el.innerHTML !== val) {
|
|
@@ -18213,6 +18817,17 @@ function OhhwellsBridge() {
|
|
|
18213
18817
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
18214
18818
|
};
|
|
18215
18819
|
applyFromCache();
|
|
18820
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18821
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18822
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18823
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18824
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18825
|
+
if (!data?.content) return;
|
|
18826
|
+
contentCache.set(subdomain, data.content);
|
|
18827
|
+
applyFromCache();
|
|
18828
|
+
}).catch(() => {
|
|
18829
|
+
});
|
|
18830
|
+
}
|
|
18216
18831
|
observer = new MutationObserver(scheduleApply);
|
|
18217
18832
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
18218
18833
|
return () => {
|
|
@@ -18237,6 +18852,10 @@ function OhhwellsBridge() {
|
|
|
18237
18852
|
deselectRef.current();
|
|
18238
18853
|
deactivateRef.current();
|
|
18239
18854
|
}, [pathname, isEditMode]);
|
|
18855
|
+
useEffect13(() => {
|
|
18856
|
+
if (!isEditMode) return;
|
|
18857
|
+
initSectionInstancesFromContent(editContentRef.current, pathname);
|
|
18858
|
+
}, [pathname, isEditMode]);
|
|
18240
18859
|
useEffect13(() => {
|
|
18241
18860
|
const contentForNav = () => {
|
|
18242
18861
|
if (isEditMode) return editContentRef.current;
|
|
@@ -18328,26 +18947,11 @@ function OhhwellsBridge() {
|
|
|
18328
18947
|
const t2 = setTimeout(measure, 500);
|
|
18329
18948
|
const ro = new ResizeObserver(schedule);
|
|
18330
18949
|
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
18950
|
return () => {
|
|
18345
18951
|
clearTimeout(t1);
|
|
18346
18952
|
clearTimeout(t2);
|
|
18347
18953
|
if (raf != null) cancelAnimationFrame(raf);
|
|
18348
18954
|
ro.disconnect();
|
|
18349
|
-
clearResizeTimers();
|
|
18350
|
-
window.removeEventListener("resize", handleResize);
|
|
18351
18955
|
};
|
|
18352
18956
|
}, [pathname, isEditMode, postToParent2]);
|
|
18353
18957
|
useEffect13(() => {
|
|
@@ -18602,9 +19206,6 @@ function OhhwellsBridge() {
|
|
|
18602
19206
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
18603
19207
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
18604
19208
|
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
19209
|
if (isInsideFloatingPanel(target)) return;
|
|
18609
19210
|
if (target.closest("[data-ohw-form-toolbar]")) return;
|
|
18610
19211
|
if (target.closest(
|
|
@@ -18612,6 +19213,9 @@ function OhhwellsBridge() {
|
|
|
18612
19213
|
)) {
|
|
18613
19214
|
return;
|
|
18614
19215
|
}
|
|
19216
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
19217
|
+
clearMediaSelectionRef.current();
|
|
19218
|
+
}
|
|
18615
19219
|
{
|
|
18616
19220
|
const formEl = getFormElement(target);
|
|
18617
19221
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -18763,14 +19367,6 @@ function OhhwellsBridge() {
|
|
|
18763
19367
|
}
|
|
18764
19368
|
const clickedButton = findClosestButtonLike(target);
|
|
18765
19369
|
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
19370
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
18775
19371
|
e.preventDefault();
|
|
18776
19372
|
e.stopPropagation();
|
|
@@ -18797,11 +19393,6 @@ function OhhwellsBridge() {
|
|
|
18797
19393
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
18798
19394
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
18799
19395
|
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
19396
|
if (navAnchor) {
|
|
18806
19397
|
e.preventDefault();
|
|
18807
19398
|
e.stopPropagation();
|
|
@@ -18971,6 +19562,9 @@ function OhhwellsBridge() {
|
|
|
18971
19562
|
setHoveredItemRect(null);
|
|
18972
19563
|
hoveredNavContainerRef.current = null;
|
|
18973
19564
|
setHoveredNavContainerRect(null);
|
|
19565
|
+
siblingHintElRef.current = null;
|
|
19566
|
+
setSiblingHintRect(null);
|
|
19567
|
+
setSiblingHintRects([]);
|
|
18974
19568
|
return;
|
|
18975
19569
|
}
|
|
18976
19570
|
{
|
|
@@ -19089,7 +19683,6 @@ function OhhwellsBridge() {
|
|
|
19089
19683
|
hoveredNavContainerRef.current = null;
|
|
19090
19684
|
setHoveredNavContainerRect(null);
|
|
19091
19685
|
hoveredItemElRef.current = editable;
|
|
19092
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
19093
19686
|
}
|
|
19094
19687
|
}
|
|
19095
19688
|
}
|
|
@@ -19386,7 +19979,7 @@ function OhhwellsBridge() {
|
|
|
19386
19979
|
}
|
|
19387
19980
|
};
|
|
19388
19981
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
19389
|
-
if (linkPopoverOpenRef.current) {
|
|
19982
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19390
19983
|
if (hoveredImageRef.current) {
|
|
19391
19984
|
hoveredImageRef.current = null;
|
|
19392
19985
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -19751,8 +20344,7 @@ function OhhwellsBridge() {
|
|
|
19751
20344
|
};
|
|
19752
20345
|
const handleMouseMove = (e) => {
|
|
19753
20346
|
const { clientX, clientY } = e;
|
|
19754
|
-
if (
|
|
19755
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
20347
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
19756
20348
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
19757
20349
|
formHoverElRef.current = null;
|
|
19758
20350
|
setFormHoverRect(null);
|
|
@@ -19760,6 +20352,12 @@ function OhhwellsBridge() {
|
|
|
19760
20352
|
setHoveredItemRect(null);
|
|
19761
20353
|
hoveredNavContainerRef.current = null;
|
|
19762
20354
|
setHoveredNavContainerRect(null);
|
|
20355
|
+
siblingHintElRef.current = null;
|
|
20356
|
+
setSiblingHintRect(null);
|
|
20357
|
+
setSiblingHintRects([]);
|
|
20358
|
+
dismissImageHover();
|
|
20359
|
+
clearImageHover();
|
|
20360
|
+
setSectionGap(null);
|
|
19763
20361
|
return;
|
|
19764
20362
|
}
|
|
19765
20363
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -19771,7 +20369,11 @@ function OhhwellsBridge() {
|
|
|
19771
20369
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
19772
20370
|
const { clientX, clientY } = e.data;
|
|
19773
20371
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
19774
|
-
if (
|
|
20372
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
20373
|
+
dismissImageHover();
|
|
20374
|
+
clearImageHover();
|
|
20375
|
+
return;
|
|
20376
|
+
}
|
|
19775
20377
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
19776
20378
|
probeSectionGapAt(clientX, clientY);
|
|
19777
20379
|
probeImageAt(clientX, clientY);
|
|
@@ -20050,6 +20652,44 @@ function OhhwellsBridge() {
|
|
|
20050
20652
|
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20051
20653
|
}, 400));
|
|
20052
20654
|
};
|
|
20655
|
+
const reapCommittedAiSections = (excludeIds) => {
|
|
20656
|
+
const aiState = parseAiSectionsState(aiSectionsRef.current);
|
|
20657
|
+
if (aiState.sections.length === 0) return [];
|
|
20658
|
+
let orderEntries = [];
|
|
20659
|
+
try {
|
|
20660
|
+
const parsed = JSON.parse(editContentRef.current[SECTION_ORDER_KEY] ?? "[]");
|
|
20661
|
+
if (Array.isArray(parsed)) orderEntries = parsed;
|
|
20662
|
+
} catch {
|
|
20663
|
+
return [];
|
|
20664
|
+
}
|
|
20665
|
+
const removedIds = orderEntries.filter((entry) => entry && typeof entry.instanceId === "string" && entry.removed).map((entry) => entry.instanceId);
|
|
20666
|
+
if (removedIds.length === 0) return [];
|
|
20667
|
+
const result = reapRemovedAiSections(aiState, parseStyleStore(stylesRef.current), removedIds, excludeIds);
|
|
20668
|
+
if (!result.changed) return [];
|
|
20669
|
+
const nodes = [];
|
|
20670
|
+
aiSectionsRef.current = serializeAiSectionsState(result.state);
|
|
20671
|
+
nodes.push({ key: AI_SECTIONS_KEY, text: aiSectionsRef.current });
|
|
20672
|
+
const reaped = new Set(result.reapedIds);
|
|
20673
|
+
const nextOrderJson = JSON.stringify(orderEntries.filter((entry) => !reaped.has(entry.instanceId)));
|
|
20674
|
+
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: nextOrderJson };
|
|
20675
|
+
setAiSectionOrder(nextOrderJson, window.location.pathname);
|
|
20676
|
+
nodes.push({ key: SECTION_ORDER_KEY, text: nextOrderJson });
|
|
20677
|
+
if (result.store) {
|
|
20678
|
+
stylesRef.current = JSON.stringify(result.store);
|
|
20679
|
+
nodes.push({ key: STYLE_STORE_KEY, text: stylesRef.current });
|
|
20680
|
+
}
|
|
20681
|
+
const nextContent = { ...editContentRef.current };
|
|
20682
|
+
for (const key of Object.keys(nextContent)) {
|
|
20683
|
+
if (key.startsWith(AI_SLOT_KEY_PREFIX) && result.slotPrefixes.some((prefix) => key.startsWith(prefix)) && nextContent[key] !== "") {
|
|
20684
|
+
nextContent[key] = "";
|
|
20685
|
+
nodes.push({ key, text: "" });
|
|
20686
|
+
}
|
|
20687
|
+
}
|
|
20688
|
+
editContentRef.current = nextContent;
|
|
20689
|
+
applyAiSectionsToDom(result.state);
|
|
20690
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20691
|
+
return nodes;
|
|
20692
|
+
};
|
|
20053
20693
|
const handleHydrate = (e) => {
|
|
20054
20694
|
if (e.data?.type !== "ow:hydrate") return;
|
|
20055
20695
|
const content = e.data.content;
|
|
@@ -20068,6 +20708,7 @@ function OhhwellsBridge() {
|
|
|
20068
20708
|
}
|
|
20069
20709
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
20070
20710
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
20711
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
20071
20712
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
20072
20713
|
}
|
|
20073
20714
|
applyBrandChrome(content);
|
|
@@ -20079,11 +20720,11 @@ function OhhwellsBridge() {
|
|
|
20079
20720
|
continue;
|
|
20080
20721
|
}
|
|
20081
20722
|
if (key === AI_SECTIONS_KEY) continue;
|
|
20723
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
20724
|
+
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
20082
20725
|
if (key === BRAND_KIT_KEY) continue;
|
|
20083
20726
|
if (key === STYLE_STORE_KEY) continue;
|
|
20084
20727
|
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
20728
|
if (applyVideoSettingNode(key, val)) continue;
|
|
20088
20729
|
if (applyCarouselNode(key, val)) continue;
|
|
20089
20730
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -20097,6 +20738,8 @@ function OhhwellsBridge() {
|
|
|
20097
20738
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
20098
20739
|
} else if (el.dataset.ohwEditable === "link") {
|
|
20099
20740
|
applyLinkHref(el, val);
|
|
20741
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
20742
|
+
applyMapQuery(el, val);
|
|
20100
20743
|
} else if (el.dataset.ohwEditable === "icon") {
|
|
20101
20744
|
applyIconMarkup(el, val);
|
|
20102
20745
|
} else if (isIconMarkupValue(val)) {
|
|
@@ -20118,6 +20761,11 @@ function OhhwellsBridge() {
|
|
|
20118
20761
|
reconcileFooterOrderFromContent(editContentRef.current);
|
|
20119
20762
|
syncNavigationDragCursorAttrs();
|
|
20120
20763
|
enforceLinkHrefs();
|
|
20764
|
+
const hydrateReapExclude = /* @__PURE__ */ new Set();
|
|
20765
|
+
const hydratePendingUndo = pendingDeleteUndoRef.current;
|
|
20766
|
+
if (hydratePendingUndo?.sectionInstanceId) hydrateReapExclude.add(hydratePendingUndo.sectionInstanceId);
|
|
20767
|
+
const reapNodes = reapCommittedAiSections(hydrateReapExclude);
|
|
20768
|
+
if (reapNodes.length > 0) postToParentRef.current({ type: "ow:change", nodes: reapNodes });
|
|
20121
20769
|
const hydratedHeight = document.body.scrollHeight;
|
|
20122
20770
|
if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
|
|
20123
20771
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
@@ -20257,12 +20905,35 @@ function OhhwellsBridge() {
|
|
|
20257
20905
|
window.addEventListener("message", handleAiSetBrand);
|
|
20258
20906
|
const handleAiSetStyles = (e) => {
|
|
20259
20907
|
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20260
|
-
|
|
20908
|
+
let value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20261
20909
|
const previous = stylesRef.current;
|
|
20910
|
+
let previousSections;
|
|
20911
|
+
const store = parseStyleStore(value);
|
|
20912
|
+
if (store) {
|
|
20913
|
+
const folded = foldAlignIntoTrees(parseAiSectionsState(aiSectionsRef.current), store);
|
|
20914
|
+
if (folded.changed) {
|
|
20915
|
+
const nextSections = serializeAiSectionsState(folded.state);
|
|
20916
|
+
if (nextSections !== aiSectionsRef.current) {
|
|
20917
|
+
previousSections = aiSectionsRef.current;
|
|
20918
|
+
aiSectionsRef.current = nextSections;
|
|
20919
|
+
applyAiSectionsToDom(folded.state);
|
|
20920
|
+
postToParentRef.current({
|
|
20921
|
+
type: "ow:change",
|
|
20922
|
+
nodes: [{ key: AI_SECTIONS_KEY, text: nextSections }]
|
|
20923
|
+
});
|
|
20924
|
+
}
|
|
20925
|
+
value = JSON.stringify(folded.store);
|
|
20926
|
+
}
|
|
20927
|
+
}
|
|
20262
20928
|
stylesRef.current = value;
|
|
20263
20929
|
applyStylesToDom(parseStyleStore(value));
|
|
20264
20930
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20265
|
-
postToParentRef.current({
|
|
20931
|
+
postToParentRef.current({
|
|
20932
|
+
type: "ow:ai-styles-applied",
|
|
20933
|
+
previous,
|
|
20934
|
+
value,
|
|
20935
|
+
...previousSections !== void 0 ? { previousSections } : {}
|
|
20936
|
+
});
|
|
20266
20937
|
};
|
|
20267
20938
|
window.addEventListener("message", handleAiSetStyles);
|
|
20268
20939
|
const handleGetBrand = (e) => {
|
|
@@ -20279,8 +20950,11 @@ function OhhwellsBridge() {
|
|
|
20279
20950
|
if (!instanceId || !direction) return;
|
|
20280
20951
|
const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
|
|
20281
20952
|
if (!entries) return;
|
|
20282
|
-
const orderJson = JSON.stringify(
|
|
20953
|
+
const orderJson = JSON.stringify(
|
|
20954
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
20955
|
+
);
|
|
20283
20956
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20957
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
20284
20958
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20285
20959
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
20286
20960
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20326,8 +21000,11 @@ function OhhwellsBridge() {
|
|
|
20326
21000
|
const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
|
|
20327
21001
|
const entries = deleteSectionInstance(instanceId, window.location.pathname, currentEntries);
|
|
20328
21002
|
if (!entries) return;
|
|
20329
|
-
const orderJson = JSON.stringify(
|
|
21003
|
+
const orderJson = JSON.stringify(
|
|
21004
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
21005
|
+
);
|
|
20330
21006
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
21007
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20331
21008
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
20332
21009
|
aiSectionApiRef.current?.clear();
|
|
20333
21010
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20336,6 +21013,7 @@ function OhhwellsBridge() {
|
|
|
20336
21013
|
const actionId = newInstanceId();
|
|
20337
21014
|
pendingDeleteUndoRef.current = {
|
|
20338
21015
|
actionId,
|
|
21016
|
+
sectionInstanceId: instanceId,
|
|
20339
21017
|
restore: () => {
|
|
20340
21018
|
const restoredEntries = getPageSectionOrderEntries(
|
|
20341
21019
|
editContentRef.current[SECTION_ORDER_KEY],
|
|
@@ -20343,8 +21021,11 @@ function OhhwellsBridge() {
|
|
|
20343
21021
|
);
|
|
20344
21022
|
const restored = restoreSectionInstance(instanceId, window.location.pathname, restoredEntries);
|
|
20345
21023
|
if (!restored) return;
|
|
20346
|
-
const restoredJson = JSON.stringify(
|
|
21024
|
+
const restoredJson = JSON.stringify(
|
|
21025
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, restored)
|
|
21026
|
+
);
|
|
20347
21027
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
|
|
21028
|
+
setAiSectionOrder(restoredJson, window.location.pathname);
|
|
20348
21029
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
|
|
20349
21030
|
window.dispatchEvent(new Event("resize"));
|
|
20350
21031
|
const restoreHeight = document.body.scrollHeight;
|
|
@@ -20370,7 +21051,9 @@ function OhhwellsBridge() {
|
|
|
20370
21051
|
const result = duplicateSectionInstance(instanceId, newId, window.location.pathname, currentEntries);
|
|
20371
21052
|
if (!result) return;
|
|
20372
21053
|
const { entries, keyRekeys } = result;
|
|
20373
|
-
const orderJson = JSON.stringify(
|
|
21054
|
+
const orderJson = JSON.stringify(
|
|
21055
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
21056
|
+
);
|
|
20374
21057
|
const nodes = [{ key: SECTION_ORDER_KEY, text: orderJson }];
|
|
20375
21058
|
for (const { from, to } of keyRekeys) {
|
|
20376
21059
|
const inherited = editContentRef.current[from];
|
|
@@ -20380,6 +21063,7 @@ function OhhwellsBridge() {
|
|
|
20380
21063
|
...editContentRef.current,
|
|
20381
21064
|
...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
|
|
20382
21065
|
};
|
|
21066
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
20383
21067
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20384
21068
|
postToParentRef.current({ type: "ow:change", nodes });
|
|
20385
21069
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20398,6 +21082,12 @@ function OhhwellsBridge() {
|
|
|
20398
21082
|
closeLinkPopoverRef.current();
|
|
20399
21083
|
return;
|
|
20400
21084
|
}
|
|
21085
|
+
if (floatingPanelOpenRef.current) {
|
|
21086
|
+
setFloatingPanelRef.current(null);
|
|
21087
|
+
deselectRef.current();
|
|
21088
|
+
deactivateRef.current();
|
|
21089
|
+
return;
|
|
21090
|
+
}
|
|
20401
21091
|
deselectRef.current();
|
|
20402
21092
|
deactivateRef.current();
|
|
20403
21093
|
clearMediaSelectionRef.current();
|
|
@@ -20643,6 +21333,10 @@ function OhhwellsBridge() {
|
|
|
20643
21333
|
};
|
|
20644
21334
|
const handleSave = (e) => {
|
|
20645
21335
|
if (e.data?.type !== "ow:save") return;
|
|
21336
|
+
const pendingUndo = pendingDeleteUndoRef.current;
|
|
21337
|
+
const reapExclude = /* @__PURE__ */ new Set();
|
|
21338
|
+
if (pendingUndo?.sectionInstanceId) reapExclude.add(pendingUndo.sectionInstanceId);
|
|
21339
|
+
const reapNodes = reapCommittedAiSections(reapExclude);
|
|
20646
21340
|
const nodes = collectEditableNodes(editContentRef.current);
|
|
20647
21341
|
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
20648
21342
|
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
@@ -20662,6 +21356,11 @@ function OhhwellsBridge() {
|
|
|
20662
21356
|
const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
|
|
20663
21357
|
if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
|
|
20664
21358
|
});
|
|
21359
|
+
for (const reapNode of reapNodes) {
|
|
21360
|
+
if (reapNode.key.startsWith(AI_SLOT_KEY_PREFIX) && !nodes.some((n) => n.key === reapNode.key)) {
|
|
21361
|
+
nodes.push({ key: reapNode.key, type: "text", text: reapNode.text });
|
|
21362
|
+
}
|
|
21363
|
+
}
|
|
20665
21364
|
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
20666
21365
|
};
|
|
20667
21366
|
const handleInsertSection = (e) => {
|
|
@@ -20672,8 +21371,12 @@ function OhhwellsBridge() {
|
|
|
20672
21371
|
if (inserted) {
|
|
20673
21372
|
const tracker = getSectionsTracker();
|
|
20674
21373
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
20675
|
-
const
|
|
20676
|
-
|
|
21374
|
+
const reportHeight = () => {
|
|
21375
|
+
const h = document.body.scrollHeight;
|
|
21376
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
21377
|
+
};
|
|
21378
|
+
reportHeight();
|
|
21379
|
+
setTimeout(reportHeight, 500);
|
|
20677
21380
|
}
|
|
20678
21381
|
};
|
|
20679
21382
|
const handleSwitchSchedule = (e) => {
|
|
@@ -21077,10 +21780,10 @@ function OhhwellsBridge() {
|
|
|
21077
21780
|
window.removeEventListener("message", handleDeleteSection);
|
|
21078
21781
|
window.removeEventListener("message", handleDuplicateSection);
|
|
21079
21782
|
window.removeEventListener("message", handleDeactivate);
|
|
21080
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
21081
21783
|
window.removeEventListener("message", handleToastAction);
|
|
21082
21784
|
window.removeEventListener("message", handleFormCount);
|
|
21083
21785
|
window.removeEventListener("message", handleUiEscape);
|
|
21786
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
21084
21787
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
21085
21788
|
autoSaveTimers.current.clear();
|
|
21086
21789
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -21283,7 +21986,7 @@ function OhhwellsBridge() {
|
|
|
21283
21986
|
postToParent2({
|
|
21284
21987
|
type: "ow:ready",
|
|
21285
21988
|
version: "1",
|
|
21286
|
-
bridgeVersion: "0.1.
|
|
21989
|
+
bridgeVersion: "0.1.92",
|
|
21287
21990
|
path: pathname,
|
|
21288
21991
|
nodes: collectEditableNodes(editContentRef.current),
|
|
21289
21992
|
sections
|
|
@@ -22202,6 +22905,59 @@ function OhhwellsBridge() {
|
|
|
22202
22905
|
) : null
|
|
22203
22906
|
] });
|
|
22204
22907
|
}
|
|
22908
|
+
|
|
22909
|
+
// src/ui/EmptySection.tsx
|
|
22910
|
+
import Link3 from "next/link";
|
|
22911
|
+
import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
22912
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
22913
|
+
return /* @__PURE__ */ jsxs21(Fragment9, { children: [
|
|
22914
|
+
/* @__PURE__ */ jsx34(
|
|
22915
|
+
"p",
|
|
22916
|
+
{
|
|
22917
|
+
style: {
|
|
22918
|
+
fontFamily: "var(--brand-font-body)",
|
|
22919
|
+
fontSize: "0.75rem",
|
|
22920
|
+
fontWeight: 500,
|
|
22921
|
+
letterSpacing: "0.15em",
|
|
22922
|
+
textTransform: "uppercase",
|
|
22923
|
+
color: "var(--brand-accent)",
|
|
22924
|
+
marginBottom: "1.5rem"
|
|
22925
|
+
},
|
|
22926
|
+
children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
22927
|
+
}
|
|
22928
|
+
),
|
|
22929
|
+
/* @__PURE__ */ jsx34(
|
|
22930
|
+
"h1",
|
|
22931
|
+
{
|
|
22932
|
+
style: {
|
|
22933
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22934
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22935
|
+
lineHeight: 1.1,
|
|
22936
|
+
letterSpacing: "-0.025em",
|
|
22937
|
+
color: "var(--brand-text)",
|
|
22938
|
+
marginBottom: "1rem"
|
|
22939
|
+
},
|
|
22940
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22941
|
+
children: title
|
|
22942
|
+
}
|
|
22943
|
+
),
|
|
22944
|
+
/* @__PURE__ */ jsx34(
|
|
22945
|
+
"p",
|
|
22946
|
+
{
|
|
22947
|
+
style: {
|
|
22948
|
+
fontFamily: "var(--brand-font-body)",
|
|
22949
|
+
fontSize: "1rem",
|
|
22950
|
+
lineHeight: 1.7,
|
|
22951
|
+
fontWeight: 300,
|
|
22952
|
+
color: "var(--brand-text-muted)",
|
|
22953
|
+
maxWidth: "340px"
|
|
22954
|
+
},
|
|
22955
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22956
|
+
children: "This page doesn't have any content yet."
|
|
22957
|
+
}
|
|
22958
|
+
)
|
|
22959
|
+
] });
|
|
22960
|
+
}
|
|
22205
22961
|
export {
|
|
22206
22962
|
AI_DEFAULT_BRAND,
|
|
22207
22963
|
AI_TREE_SCHEMA_VERSIONS,
|
|
@@ -22218,6 +22974,7 @@ export {
|
|
|
22218
22974
|
DropdownMenuItem,
|
|
22219
22975
|
DropdownMenuSeparator,
|
|
22220
22976
|
DropdownMenuTrigger,
|
|
22977
|
+
EmptySection,
|
|
22221
22978
|
ItemActionToolbar,
|
|
22222
22979
|
ItemInteractionLayer,
|
|
22223
22980
|
LinkEditorPanel,
|