@ohhwells/bridge 0.1.87 → 0.1.88-next.262
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 +1152 -377
- 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 +1151 -377
- 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,510 @@ 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 topLevelSections() {
|
|
636
|
+
return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
637
|
+
(el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isRemovedSection(el)
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
function instanceIdOf(el) {
|
|
641
|
+
return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
|
|
642
|
+
}
|
|
643
|
+
function findByInstanceId(instanceId) {
|
|
644
|
+
const escapedId = CSS.escape(instanceId);
|
|
645
|
+
return document.querySelector(`[data-ohw-instance="${escapedId}"]`) ?? document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
|
|
646
|
+
}
|
|
647
|
+
function planSectionMove(instanceId, targetIndex, currentPath) {
|
|
648
|
+
const sections = topLevelSections();
|
|
649
|
+
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
650
|
+
if (index === -1) return null;
|
|
651
|
+
const dragged = sections[index];
|
|
652
|
+
const others = sections.filter((_, i) => i !== index);
|
|
653
|
+
const clamped = Math.max(0, Math.min(targetIndex, others.length));
|
|
654
|
+
const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
|
|
655
|
+
return reordered.map((el, order) => ({
|
|
656
|
+
instanceId: instanceIdOf(el),
|
|
657
|
+
type: el.getAttribute("data-ohw-section") ?? "",
|
|
658
|
+
order,
|
|
659
|
+
pagePath: currentPath
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
function moveSectionInstance(instanceId, direction, currentPath) {
|
|
663
|
+
const sections = topLevelSections();
|
|
664
|
+
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
665
|
+
if (index === -1) return null;
|
|
666
|
+
const siblingIndex = direction === "up" ? index - 1 : index + 1;
|
|
667
|
+
if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
|
|
668
|
+
const entries = planSectionMove(instanceId, siblingIndex, currentPath);
|
|
669
|
+
if (!entries) return null;
|
|
670
|
+
applyPersistedOrder(entries);
|
|
671
|
+
return entries;
|
|
672
|
+
}
|
|
673
|
+
function syncRemovedFlags(entries) {
|
|
674
|
+
const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
|
|
675
|
+
document.querySelectorAll(`[${REMOVED_ATTR}]`).forEach((el) => {
|
|
676
|
+
if (!removedIds.has(instanceIdOf(el))) {
|
|
677
|
+
el.style.removeProperty("display");
|
|
678
|
+
el.removeAttribute(REMOVED_ATTR);
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
for (const id of removedIds) {
|
|
682
|
+
const el = findByInstanceId(id);
|
|
683
|
+
if (el) {
|
|
684
|
+
el.style.display = "none";
|
|
685
|
+
el.setAttribute(REMOVED_ATTR, "");
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function applyPersistedOrder(entries) {
|
|
690
|
+
syncRemovedFlags(entries);
|
|
691
|
+
if (entries.length === 0) return;
|
|
692
|
+
const sections = topLevelSections();
|
|
693
|
+
if (sections.length === 0) return;
|
|
694
|
+
const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
|
|
695
|
+
const ordered = [...sections].sort((a, b) => {
|
|
696
|
+
const aOrder = orderIndex.get(instanceIdOf(a));
|
|
697
|
+
const bOrder = orderIndex.get(instanceIdOf(b));
|
|
698
|
+
if (aOrder === void 0 && bOrder === void 0) return 0;
|
|
699
|
+
if (aOrder === void 0) return 1;
|
|
700
|
+
if (bOrder === void 0) return -1;
|
|
701
|
+
return aOrder - bOrder;
|
|
702
|
+
});
|
|
703
|
+
let prev = null;
|
|
704
|
+
for (const el of ordered) {
|
|
705
|
+
if (prev) prev.after(el);
|
|
706
|
+
prev = el;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
|
|
710
|
+
if (!findByInstanceId(instanceId)) return null;
|
|
711
|
+
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
712
|
+
const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
713
|
+
(el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
|
|
714
|
+
);
|
|
715
|
+
allSections.forEach((el, order) => {
|
|
716
|
+
const id = instanceIdOf(el);
|
|
717
|
+
if (!byId.has(id)) {
|
|
718
|
+
byId.set(id, { instanceId: id, type: el.getAttribute("data-ohw-section") ?? "", order, pagePath: currentPath });
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
const target = byId.get(instanceId);
|
|
722
|
+
if (!target) return null;
|
|
723
|
+
byId.set(instanceId, { ...target, removed });
|
|
724
|
+
const entries = Array.from(byId.values());
|
|
725
|
+
applyPersistedOrder(entries);
|
|
726
|
+
return entries;
|
|
727
|
+
}
|
|
728
|
+
function deleteSectionInstance(instanceId, currentPath, existingEntries) {
|
|
729
|
+
return setSectionRemoved(instanceId, currentPath, existingEntries, true);
|
|
730
|
+
}
|
|
731
|
+
function restoreSectionInstance(instanceId, currentPath, existingEntries) {
|
|
732
|
+
return setSectionRemoved(instanceId, currentPath, existingEntries, false);
|
|
733
|
+
}
|
|
734
|
+
function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
|
|
735
|
+
const original = findByInstanceId(instanceId);
|
|
736
|
+
if (!original) return null;
|
|
737
|
+
const clone = original.cloneNode(true);
|
|
738
|
+
clone.setAttribute("data-ohw-instance", newId);
|
|
739
|
+
const keyRekeys = rekeySectionSubtree(clone, newId);
|
|
740
|
+
original.insertAdjacentElement("afterend", clone);
|
|
741
|
+
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
742
|
+
const entries = topLevelSections().map((el, order) => {
|
|
743
|
+
const id = instanceIdOf(el);
|
|
744
|
+
return {
|
|
745
|
+
instanceId: id,
|
|
746
|
+
type: el.getAttribute("data-ohw-section") ?? "",
|
|
747
|
+
order,
|
|
748
|
+
pagePath: currentPath,
|
|
749
|
+
...byId.get(id)?.removed ? { removed: true } : {}
|
|
750
|
+
};
|
|
751
|
+
});
|
|
752
|
+
applyPersistedOrder(entries);
|
|
753
|
+
return { entries, keyRekeys };
|
|
754
|
+
}
|
|
755
|
+
function newInstanceId() {
|
|
756
|
+
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
757
|
+
}
|
|
758
|
+
function getPageSectionOrderEntries(raw, currentPath) {
|
|
759
|
+
if (!raw) return [];
|
|
760
|
+
try {
|
|
761
|
+
const entries = JSON.parse(raw);
|
|
762
|
+
return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
|
|
763
|
+
} catch {
|
|
764
|
+
return [];
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
function mergePageSectionOrder(raw, currentPath, pageEntries) {
|
|
768
|
+
let all = [];
|
|
769
|
+
if (raw) {
|
|
770
|
+
try {
|
|
771
|
+
const parsed = JSON.parse(raw);
|
|
772
|
+
if (Array.isArray(parsed)) all = parsed;
|
|
773
|
+
} catch {
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
const otherPages = all.filter((e) => e && e.pagePath && e.pagePath !== currentPath);
|
|
777
|
+
const pageIds = new Set(pageEntries.map((e) => e.instanceId));
|
|
778
|
+
const removedHere = all.filter(
|
|
779
|
+
(e) => e && (!e.pagePath || e.pagePath === currentPath) && e.removed && !pageIds.has(e.instanceId)
|
|
780
|
+
);
|
|
781
|
+
return [...otherPages, ...removedHere, ...pageEntries];
|
|
782
|
+
}
|
|
783
|
+
function rekeySectionSubtree(root, instanceId) {
|
|
784
|
+
const suffix = `::${instanceId}`;
|
|
785
|
+
const pairs = [];
|
|
786
|
+
const rekey = (el, attr) => {
|
|
787
|
+
const current = el.getAttribute(attr);
|
|
788
|
+
if (!current) return;
|
|
789
|
+
const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
|
|
790
|
+
const next = `${base}${suffix}`;
|
|
791
|
+
el.setAttribute(attr, next);
|
|
792
|
+
pairs.push({ from: current, to: next });
|
|
793
|
+
};
|
|
794
|
+
if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
|
|
795
|
+
if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
|
|
796
|
+
root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
|
|
797
|
+
root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
|
|
798
|
+
return pairs;
|
|
799
|
+
}
|
|
800
|
+
function initSectionInstancesFromContent(content, currentPath) {
|
|
801
|
+
document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
|
|
802
|
+
el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
|
|
803
|
+
});
|
|
804
|
+
const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
|
|
805
|
+
for (const entry of entries) {
|
|
806
|
+
if (entry.instanceId === entry.type) continue;
|
|
807
|
+
if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
|
|
808
|
+
const original = document.querySelector(
|
|
809
|
+
`[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
|
|
810
|
+
);
|
|
811
|
+
if (!original) continue;
|
|
812
|
+
const clone = original.cloneNode(true);
|
|
813
|
+
clone.setAttribute("data-ohw-instance", entry.instanceId);
|
|
814
|
+
rekeySectionSubtree(clone, entry.instanceId);
|
|
815
|
+
original.insertAdjacentElement("afterend", clone);
|
|
816
|
+
}
|
|
817
|
+
applyPersistedOrder(entries);
|
|
818
|
+
}
|
|
819
|
+
|
|
471
820
|
// src/ui/ai-tree/AiTreeRenderer.tsx
|
|
472
821
|
import React from "react";
|
|
473
822
|
import { ArrowLeft, ArrowRight, ChevronDown, icons as lucideIcons } from "lucide-react";
|
|
823
|
+
|
|
824
|
+
// src/lib/placeholder-imagery.ts
|
|
825
|
+
var U = (id) => `https://images.unsplash.com/photo-${id}?auto=format&fit=crop&w=1600&q=80`;
|
|
826
|
+
var GENERIC = [
|
|
827
|
+
U("1441986300917-64674bd600d8"),
|
|
828
|
+
U("1486406146926-c627a92ad1ab"),
|
|
829
|
+
U("1497032628192-86f99bcd76bc"),
|
|
830
|
+
U("1521737604893-d14cc237f11d"),
|
|
831
|
+
U("1522071820081-009f0129c71c"),
|
|
832
|
+
U("1519389950473-47ba0277781c"),
|
|
833
|
+
U("1460925895917-afdab827c52f"),
|
|
834
|
+
U("1504384308090-c894fdcc538d")
|
|
835
|
+
];
|
|
836
|
+
var PEOPLE = [
|
|
837
|
+
U("1500648767791-00dcc994a43e"),
|
|
838
|
+
U("1494790108377-be9c29b29330"),
|
|
839
|
+
U("1507003211169-0a1dd7228f2d"),
|
|
840
|
+
U("1438761681033-6461ffad8d80"),
|
|
841
|
+
U("1544005313-94ddf0286df2"),
|
|
842
|
+
U("1472099645785-5658abf4ff4e"),
|
|
843
|
+
U("1519085360753-af0119f7cbe7"),
|
|
844
|
+
U("1534528741775-53994a69daeb")
|
|
845
|
+
];
|
|
846
|
+
var THEMED = [
|
|
847
|
+
{
|
|
848
|
+
keywords: ["portrait", "headshot", "person", "people", "team", "staff", "avatar", "founder", "face"],
|
|
849
|
+
pool: PEOPLE
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
keywords: ["pet", "dog", "cat", "puppy", "kitten", "vet", "animal"],
|
|
853
|
+
pool: [
|
|
854
|
+
U("1548199973-03cce0bbc87b"),
|
|
855
|
+
U("1450778869180-41d0601e046e"),
|
|
856
|
+
U("1583511655857-d19b40a7a54e"),
|
|
857
|
+
U("1587300003388-59208cc962cb"),
|
|
858
|
+
U("1517849845537-4d257902454a"),
|
|
859
|
+
U("1601758228041-f3b2795255f1")
|
|
860
|
+
]
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
keywords: [
|
|
864
|
+
"baker",
|
|
865
|
+
"bakery",
|
|
866
|
+
"cafe",
|
|
867
|
+
"coffee",
|
|
868
|
+
"latte",
|
|
869
|
+
"restaurant",
|
|
870
|
+
"pastr",
|
|
871
|
+
"bread",
|
|
872
|
+
"cake",
|
|
873
|
+
"cater",
|
|
874
|
+
"chef",
|
|
875
|
+
"kitchen",
|
|
876
|
+
"food",
|
|
877
|
+
"pizza",
|
|
878
|
+
"dessert",
|
|
879
|
+
"brunch",
|
|
880
|
+
"bistro",
|
|
881
|
+
"deli",
|
|
882
|
+
"dish",
|
|
883
|
+
"menu"
|
|
884
|
+
],
|
|
885
|
+
pool: [
|
|
886
|
+
U("1509440159596-0249088772ff"),
|
|
887
|
+
U("1555507036-ab1f4038808a"),
|
|
888
|
+
U("1517433670267-08bbd4be890f"),
|
|
889
|
+
U("1486427944299-d1955d23e34d"),
|
|
890
|
+
U("1504754524776-8f4f37790ca0"),
|
|
891
|
+
U("1495474472287-4d71bcdd2085"),
|
|
892
|
+
U("1521017432531-fbd92d768814"),
|
|
893
|
+
U("1556909114-f6e7ad7d3136")
|
|
894
|
+
]
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
keywords: [
|
|
898
|
+
"shop",
|
|
899
|
+
"store",
|
|
900
|
+
"boutique",
|
|
901
|
+
"retail",
|
|
902
|
+
"clothing",
|
|
903
|
+
"fashion",
|
|
904
|
+
"jewel",
|
|
905
|
+
"gift",
|
|
906
|
+
"florist",
|
|
907
|
+
"market",
|
|
908
|
+
"grocer",
|
|
909
|
+
"product",
|
|
910
|
+
"storefront"
|
|
911
|
+
],
|
|
912
|
+
pool: [
|
|
913
|
+
U("1441984904996-e0b6ba687e04"),
|
|
914
|
+
U("1472851294608-062f824d29cc"),
|
|
915
|
+
U("1523381210434-271e8be1f52b"),
|
|
916
|
+
U("1534452203293-494d7ddbf7e0"),
|
|
917
|
+
U("1445205170230-053b83016050"),
|
|
918
|
+
U("1560243563-062bfc001d68")
|
|
919
|
+
]
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
keywords: [
|
|
923
|
+
"yoga",
|
|
924
|
+
"pilates",
|
|
925
|
+
"fitness",
|
|
926
|
+
"gym",
|
|
927
|
+
"workout",
|
|
928
|
+
"trainer",
|
|
929
|
+
"wellness",
|
|
930
|
+
"meditat",
|
|
931
|
+
"massage",
|
|
932
|
+
"therap",
|
|
933
|
+
"physio",
|
|
934
|
+
"chiro",
|
|
935
|
+
"nutrition",
|
|
936
|
+
"spa",
|
|
937
|
+
"studio"
|
|
938
|
+
],
|
|
939
|
+
pool: [
|
|
940
|
+
U("1544367567-0f2fcb009e0b"),
|
|
941
|
+
U("1506126613408-eca07ce68773"),
|
|
942
|
+
U("1545205597-3d9d02c29597"),
|
|
943
|
+
U("1552196563-55cd4e45efb3"),
|
|
944
|
+
U("1518611012118-696072aa579a"),
|
|
945
|
+
U("1571019613454-1cb2f99b2d8b"),
|
|
946
|
+
U("1540555700478-4be289fbecef"),
|
|
947
|
+
U("1519824145371-296894a0daa9")
|
|
948
|
+
]
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
keywords: [
|
|
952
|
+
"salon",
|
|
953
|
+
"hairdress",
|
|
954
|
+
"haircut",
|
|
955
|
+
"barber",
|
|
956
|
+
"manicure",
|
|
957
|
+
"pedicure",
|
|
958
|
+
"nails",
|
|
959
|
+
"beauty",
|
|
960
|
+
"makeup",
|
|
961
|
+
"cosmetic",
|
|
962
|
+
"eyelash",
|
|
963
|
+
"eyebrow",
|
|
964
|
+
"skincare",
|
|
965
|
+
"esthetic",
|
|
966
|
+
"waxing",
|
|
967
|
+
"hair"
|
|
968
|
+
],
|
|
969
|
+
pool: [
|
|
970
|
+
U("1560066984-138dadb4c035"),
|
|
971
|
+
U("1522337660859-02fbefca4702"),
|
|
972
|
+
U("1562322140-8baeececf3df"),
|
|
973
|
+
U("1521590832167-7bcbfaa6381f"),
|
|
974
|
+
U("1487412947147-5cebf100ffc2"),
|
|
975
|
+
U("1526045478516-99145907023c")
|
|
976
|
+
]
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
keywords: [
|
|
980
|
+
"cleaning",
|
|
981
|
+
"plumb",
|
|
982
|
+
"electric",
|
|
983
|
+
"landscap",
|
|
984
|
+
"contractor",
|
|
985
|
+
"handyman",
|
|
986
|
+
"renov",
|
|
987
|
+
"hvac",
|
|
988
|
+
"roofing",
|
|
989
|
+
"painting",
|
|
990
|
+
"carpentry",
|
|
991
|
+
"flooring",
|
|
992
|
+
"movers",
|
|
993
|
+
"construction",
|
|
994
|
+
"tools"
|
|
995
|
+
],
|
|
996
|
+
pool: [
|
|
997
|
+
U("1581578731548-c64695cc6952"),
|
|
998
|
+
U("1504307651254-35680f356dfd"),
|
|
999
|
+
U("1581092160562-40aa08e78837"),
|
|
1000
|
+
U("1621905251189-08b45d6a269e"),
|
|
1001
|
+
U("1558618666-fcd25c85cd64"),
|
|
1002
|
+
U("1585128792020-803d29415281")
|
|
1003
|
+
]
|
|
1004
|
+
},
|
|
1005
|
+
{
|
|
1006
|
+
keywords: [
|
|
1007
|
+
"legal",
|
|
1008
|
+
"attorney",
|
|
1009
|
+
"lawyer",
|
|
1010
|
+
"account",
|
|
1011
|
+
"bookkeep",
|
|
1012
|
+
"consult",
|
|
1013
|
+
"coaching",
|
|
1014
|
+
"financ",
|
|
1015
|
+
"insurance",
|
|
1016
|
+
"realtor",
|
|
1017
|
+
"estate",
|
|
1018
|
+
"marketing",
|
|
1019
|
+
"agency",
|
|
1020
|
+
"office",
|
|
1021
|
+
"business",
|
|
1022
|
+
"desk"
|
|
1023
|
+
],
|
|
1024
|
+
pool: [
|
|
1025
|
+
U("1497366216548-37526070297c"),
|
|
1026
|
+
U("1497366811353-6870744d04b2"),
|
|
1027
|
+
U("1454165804606-c3d57bc86b40"),
|
|
1028
|
+
U("1521791136064-7986c2920216"),
|
|
1029
|
+
U("1556761175-b413da4baf72"),
|
|
1030
|
+
U("1542744173-8e7e53415bb0")
|
|
1031
|
+
]
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
keywords: [
|
|
1035
|
+
"wedding",
|
|
1036
|
+
"event",
|
|
1037
|
+
"party",
|
|
1038
|
+
"celebrat",
|
|
1039
|
+
"venue",
|
|
1040
|
+
"community",
|
|
1041
|
+
"nonprofit",
|
|
1042
|
+
"charity",
|
|
1043
|
+
"workshop",
|
|
1044
|
+
"photograph",
|
|
1045
|
+
"concert"
|
|
1046
|
+
],
|
|
1047
|
+
pool: [
|
|
1048
|
+
U("1511578314322-379afb476865"),
|
|
1049
|
+
U("1501281668745-f7f57925c3b4"),
|
|
1050
|
+
U("1523580494863-6f3031224c94"),
|
|
1051
|
+
U("1540575467063-178a50c2df87"),
|
|
1052
|
+
U("1505236858219-8359eb29e329"),
|
|
1053
|
+
U("1528605248644-14dd04022da1")
|
|
1054
|
+
]
|
|
1055
|
+
}
|
|
1056
|
+
];
|
|
1057
|
+
function poolForSubject(subject) {
|
|
1058
|
+
for (const theme of THEMED) {
|
|
1059
|
+
if (theme.keywords.some((k) => subject.includes(k))) {
|
|
1060
|
+
return theme.pool;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return GENERIC;
|
|
1064
|
+
}
|
|
1065
|
+
function mixedHash(text) {
|
|
1066
|
+
let hash = 2166136261;
|
|
1067
|
+
for (let i = 0; i < text.length; i++) {
|
|
1068
|
+
hash ^= text.charCodeAt(i);
|
|
1069
|
+
hash = Math.imul(hash, 16777619);
|
|
1070
|
+
}
|
|
1071
|
+
return hash >>> 16 & 65535;
|
|
1072
|
+
}
|
|
1073
|
+
function resolvePlaceholderRef(ref) {
|
|
1074
|
+
const match = /^placeholder:([a-z0-9-]+)$/.exec(ref);
|
|
1075
|
+
if (!match) return null;
|
|
1076
|
+
const subject = match[1];
|
|
1077
|
+
const pool = poolForSubject(subject.replace(/-\d+$/, ""));
|
|
1078
|
+
return pool[mixedHash(ref) % pool.length];
|
|
1079
|
+
}
|
|
1080
|
+
function collectPlaceholderRefs(tree) {
|
|
1081
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1082
|
+
for (const match of JSON.stringify(tree ?? null).matchAll(/"(placeholder:[a-z0-9-]+)"/gu)) {
|
|
1083
|
+
seen.add(match[1]);
|
|
1084
|
+
}
|
|
1085
|
+
return [...seen];
|
|
1086
|
+
}
|
|
1087
|
+
function buildPlaceholderMap(tree) {
|
|
1088
|
+
const map = {};
|
|
1089
|
+
const cursor = /* @__PURE__ */ new Map();
|
|
1090
|
+
for (const ref of collectPlaceholderRefs(tree)) {
|
|
1091
|
+
const subject = ref.slice("placeholder:".length).replace(/-\d+$/, "");
|
|
1092
|
+
const pool = poolForSubject(subject);
|
|
1093
|
+
const start = cursor.get(pool) ?? mixedHash(ref) % pool.length;
|
|
1094
|
+
map[ref] = pool[start % pool.length];
|
|
1095
|
+
cursor.set(pool, start + 1);
|
|
1096
|
+
}
|
|
1097
|
+
return map;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// src/ui/ai-tree/AiTreeRenderer.tsx
|
|
474
1101
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
475
1102
|
function lucideByName(name) {
|
|
476
1103
|
const pascal = name.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
@@ -484,6 +1111,7 @@ var typeStyle = (spec, font) => ({
|
|
|
484
1111
|
fontWeight: spec.weight
|
|
485
1112
|
});
|
|
486
1113
|
var str = (value) => typeof value === "string" ? value : "";
|
|
1114
|
+
var cardRadius = (slots) => slots.cornerStyle === "sharp" ? 0 : AI_TREE_TOKENS.radiusCard;
|
|
487
1115
|
var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.trim()).filter(Boolean);
|
|
488
1116
|
var CHECK_MASK = `data:image/svg+xml,${encodeURIComponent(
|
|
489
1117
|
'<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 +1138,25 @@ var FEATURE_LINE_CSS = [
|
|
|
510
1138
|
`background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
|
|
511
1139
|
`mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
|
|
512
1140
|
].join("");
|
|
1141
|
+
function buttonShellStyle(ctx, fullWidth) {
|
|
1142
|
+
const bs = ctx.buttonStyle;
|
|
1143
|
+
if (bs) {
|
|
1144
|
+
return {
|
|
1145
|
+
borderRadius: bs.radius,
|
|
1146
|
+
...bs.padding ? { padding: bs.padding } : {},
|
|
1147
|
+
...bs.fontFamily ? { fontFamily: bs.fontFamily } : { fontFamily: ctx.brand.fonts.body },
|
|
1148
|
+
...bs.fontSize ? { fontSize: bs.fontSize } : {},
|
|
1149
|
+
...bs.fontWeight ? { fontWeight: bs.fontWeight } : {},
|
|
1150
|
+
...bs.letterSpacing && bs.letterSpacing !== "normal" ? { letterSpacing: bs.letterSpacing } : {},
|
|
1151
|
+
...bs.textTransform && bs.textTransform !== "none" ? { textTransform: bs.textTransform } : {}
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
return {
|
|
1155
|
+
borderRadius: AI_TREE_TOKENS.radiusButton,
|
|
1156
|
+
padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
|
|
1157
|
+
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
513
1160
|
function hexLuminance(color) {
|
|
514
1161
|
const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
515
1162
|
if (!m) return null;
|
|
@@ -526,6 +1173,12 @@ function hexContrast(a, b) {
|
|
|
526
1173
|
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
527
1174
|
return (hi + 0.05) / (lo + 0.05);
|
|
528
1175
|
}
|
|
1176
|
+
function primaryButtonLabel(brand) {
|
|
1177
|
+
const darkC = hexContrast(brand.palette.primary, brand.palette.dark);
|
|
1178
|
+
const lightC = hexContrast(brand.palette.primary, AI_TREE_TOKENS.textPrimaryForeground);
|
|
1179
|
+
if (darkC === null || lightC === null) return AI_TREE_TOKENS.textPrimaryForeground;
|
|
1180
|
+
return darkC > lightC ? brand.palette.dark : AI_TREE_TOKENS.textPrimaryForeground;
|
|
1181
|
+
}
|
|
529
1182
|
function accentBandContext(brand) {
|
|
530
1183
|
const p = brand.palette;
|
|
531
1184
|
const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
|
|
@@ -543,6 +1196,22 @@ function accentBandContext(brand) {
|
|
|
543
1196
|
function textAttrs(ctx, path) {
|
|
544
1197
|
return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
|
|
545
1198
|
}
|
|
1199
|
+
var AI_RESPONSIVE_CSS = [
|
|
1200
|
+
"@media (max-width: 960px) {",
|
|
1201
|
+
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
|
|
1202
|
+
' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
|
|
1203
|
+
"}",
|
|
1204
|
+
"@media (max-width: 640px) {",
|
|
1205
|
+
" [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
|
|
1206
|
+
" [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
|
|
1207
|
+
// Group containers flatten to a column on phones; span placements come along for free.
|
|
1208
|
+
" [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
|
|
1209
|
+
" [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
|
|
1210
|
+
" [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
|
|
1211
|
+
" [data-ai-responsive] { overflow-x: hidden; }",
|
|
1212
|
+
" [data-ai-responsive] img { max-width: 100%; }",
|
|
1213
|
+
"}"
|
|
1214
|
+
].join("\n");
|
|
546
1215
|
var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
|
547
1216
|
function MediaBox({
|
|
548
1217
|
refValue,
|
|
@@ -555,13 +1224,17 @@ function MediaBox({
|
|
|
555
1224
|
const url = refValue ? ctx.resolveMedia(refValue) : null;
|
|
556
1225
|
const isIcon = /^(lucide|simple):/.test(refValue);
|
|
557
1226
|
const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
|
|
558
|
-
const editAttrs = ctx.keyFor && editPath
|
|
1227
|
+
const editAttrs = ctx.keyFor && editPath ? {
|
|
1228
|
+
"data-ohw-key": ctx.keyFor(editPath),
|
|
1229
|
+
"data-ohw-editable": isIcon ? "icon" : "image"
|
|
1230
|
+
} : {};
|
|
559
1231
|
if (isIcon) {
|
|
560
1232
|
const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
|
|
561
1233
|
return /* @__PURE__ */ jsx(
|
|
562
1234
|
"span",
|
|
563
1235
|
{
|
|
564
1236
|
"data-ai-icon": refValue,
|
|
1237
|
+
...editAttrs,
|
|
565
1238
|
style: {
|
|
566
1239
|
display: "inline-flex",
|
|
567
1240
|
width: 48,
|
|
@@ -625,12 +1298,10 @@ function ButtonEl({
|
|
|
625
1298
|
width: fullWidth ? "100%" : void 0,
|
|
626
1299
|
alignItems: "center",
|
|
627
1300
|
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
1301
|
textDecoration: "none",
|
|
631
1302
|
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 ??
|
|
1303
|
+
...buttonShellStyle(ctx, fullWidth),
|
|
1304
|
+
...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
1305
|
},
|
|
635
1306
|
children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
|
|
636
1307
|
}
|
|
@@ -656,7 +1327,7 @@ function TextBlock({ slots, ctx, path }) {
|
|
|
656
1327
|
}
|
|
657
1328
|
function SectionHeaderBlock({ node, ctx, path }) {
|
|
658
1329
|
const slots = node.slots ?? {};
|
|
659
|
-
const align = slots.alignment === "center" ? "center" : "left";
|
|
1330
|
+
const align = node.align ?? (slots.alignment === "center" ? "center" : "left");
|
|
660
1331
|
const children = node.children ?? [];
|
|
661
1332
|
const buttonRowIdx = children.findIndex((c) => c.type === "button-row");
|
|
662
1333
|
const buttonRow = buttonRowIdx >= 0 ? children[buttonRowIdx] : void 0;
|
|
@@ -700,7 +1371,7 @@ function SectionHeaderBlock({ node, ctx, path }) {
|
|
|
700
1371
|
display: "flex",
|
|
701
1372
|
gap: AI_TREE_TOKENS.spacing6,
|
|
702
1373
|
marginTop: AI_TREE_TOKENS.spacing8,
|
|
703
|
-
justifyContent: align === "center" ? "center" : "flex-start"
|
|
1374
|
+
justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start"
|
|
704
1375
|
},
|
|
705
1376
|
children: (buttonRow.children ?? []).map((button, i) => /* @__PURE__ */ jsx(
|
|
706
1377
|
ButtonEl,
|
|
@@ -806,10 +1477,11 @@ function PricingCard({ node, ctx, path }) {
|
|
|
806
1477
|
return /* @__PURE__ */ jsxs(
|
|
807
1478
|
"div",
|
|
808
1479
|
{
|
|
1480
|
+
"data-ohw-card": "",
|
|
809
1481
|
style: {
|
|
810
1482
|
background: hasBg ? ctx.brand.palette.light : "transparent",
|
|
811
1483
|
border: `1px solid ${dark}`,
|
|
812
|
-
borderRadius:
|
|
1484
|
+
borderRadius: cardRadius(slots),
|
|
813
1485
|
padding: AI_TREE_TOKENS.paddingBlock,
|
|
814
1486
|
display: "flex",
|
|
815
1487
|
flexDirection: "column",
|
|
@@ -914,10 +1586,11 @@ function TestimonialCard({ node, ctx, path }) {
|
|
|
914
1586
|
return /* @__PURE__ */ jsx(
|
|
915
1587
|
"div",
|
|
916
1588
|
{
|
|
1589
|
+
"data-ohw-card": "",
|
|
917
1590
|
"data-ai-avatar-pos": avatarPos ?? void 0,
|
|
918
1591
|
style: {
|
|
919
1592
|
background: hasBg ? ctx.cardSurface : "transparent",
|
|
920
|
-
borderRadius: hasBg ?
|
|
1593
|
+
borderRadius: hasBg ? cardRadius(slots) : 0,
|
|
921
1594
|
overflow: "hidden",
|
|
922
1595
|
outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
|
|
923
1596
|
minWidth: 0
|
|
@@ -952,10 +1625,11 @@ function TeamCard({ node, ctx, path }) {
|
|
|
952
1625
|
return /* @__PURE__ */ jsxs(
|
|
953
1626
|
"div",
|
|
954
1627
|
{
|
|
1628
|
+
"data-ohw-card": "",
|
|
955
1629
|
"data-ai-avatar-pos": avatarPos ?? void 0,
|
|
956
1630
|
style: {
|
|
957
1631
|
background: hasBg ? ctx.cardSurface : "transparent",
|
|
958
|
-
borderRadius: hasBg ?
|
|
1632
|
+
borderRadius: hasBg ? cardRadius(slots) : 0,
|
|
959
1633
|
overflow: "hidden",
|
|
960
1634
|
outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
|
|
961
1635
|
minWidth: 0,
|
|
@@ -1033,7 +1707,7 @@ function CardBlock({ node, ctx, path }) {
|
|
|
1033
1707
|
editPath: `${path}.media`
|
|
1034
1708
|
}
|
|
1035
1709
|
) : null;
|
|
1036
|
-
const centered = slots.alignment === "center";
|
|
1710
|
+
const centered = (node.align ?? slots.alignment) === "center";
|
|
1037
1711
|
const content = /* @__PURE__ */ jsxs(
|
|
1038
1712
|
"div",
|
|
1039
1713
|
{
|
|
@@ -1126,9 +1800,10 @@ function CardBlock({ node, ctx, path }) {
|
|
|
1126
1800
|
return /* @__PURE__ */ jsxs(
|
|
1127
1801
|
"div",
|
|
1128
1802
|
{
|
|
1803
|
+
"data-ohw-card": "",
|
|
1129
1804
|
style: {
|
|
1130
1805
|
background: hasBg ? ctx.cardSurface : "transparent",
|
|
1131
|
-
borderRadius: hasBg ?
|
|
1806
|
+
borderRadius: hasBg ? cardRadius(slots) : 0,
|
|
1132
1807
|
overflow: "hidden",
|
|
1133
1808
|
outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
|
|
1134
1809
|
display: horizontal ? "flex" : "block",
|
|
@@ -1156,7 +1831,7 @@ function CardBlock({ node, ctx, path }) {
|
|
|
1156
1831
|
) : /* @__PURE__ */ jsx(
|
|
1157
1832
|
"div",
|
|
1158
1833
|
{
|
|
1159
|
-
style: /^(lucide|simple):/.test(mediaRef) ? { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` } : hasBg ? void 0 : { borderRadius:
|
|
1834
|
+
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
1835
|
children: media
|
|
1161
1836
|
}
|
|
1162
1837
|
)),
|
|
@@ -1447,7 +2122,7 @@ function CollectionBlock({ node, ctx, path }) {
|
|
|
1447
2122
|
return /* @__PURE__ */ jsx(
|
|
1448
2123
|
"div",
|
|
1449
2124
|
{
|
|
1450
|
-
"data-ai-grid":
|
|
2125
|
+
"data-ai-grid": String(itemsPerRow),
|
|
1451
2126
|
style: {
|
|
1452
2127
|
display: "grid",
|
|
1453
2128
|
gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
|
|
@@ -1567,6 +2242,32 @@ function renderNode(node, ctx, path) {
|
|
|
1567
2242
|
if (child) {
|
|
1568
2243
|
return renderNode(child, ctx, `${path}.c0`);
|
|
1569
2244
|
}
|
|
2245
|
+
if (str(slots.provider) === "map" && str(slots.query)) {
|
|
2246
|
+
const query = str(slots.query);
|
|
2247
|
+
const mapAttrs = ctx.keyFor ? {
|
|
2248
|
+
"data-ohw-key": ctx.keyFor(`${path}.query`),
|
|
2249
|
+
"data-ohw-editable": "map",
|
|
2250
|
+
"data-ohw-map-query": query
|
|
2251
|
+
} : {};
|
|
2252
|
+
return /* @__PURE__ */ jsx(
|
|
2253
|
+
"iframe",
|
|
2254
|
+
{
|
|
2255
|
+
...mapAttrs,
|
|
2256
|
+
"data-ai-embed": "map",
|
|
2257
|
+
title: str(slots.title) || "Map",
|
|
2258
|
+
src: `https://www.google.com/maps?q=${encodeURIComponent(query)}&output=embed`,
|
|
2259
|
+
loading: "lazy",
|
|
2260
|
+
referrerPolicy: "no-referrer-when-downgrade",
|
|
2261
|
+
style: {
|
|
2262
|
+
width: "100%",
|
|
2263
|
+
minHeight: 320,
|
|
2264
|
+
border: 0,
|
|
2265
|
+
borderRadius: AI_TREE_TOKENS.radiusCard,
|
|
2266
|
+
display: "block"
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
1570
2271
|
return /* @__PURE__ */ jsx(
|
|
1571
2272
|
"div",
|
|
1572
2273
|
{
|
|
@@ -1670,15 +2371,12 @@ function renderNode(node, ctx, path) {
|
|
|
1670
2371
|
alignSelf: submitAlign,
|
|
1671
2372
|
border: "none",
|
|
1672
2373
|
cursor: "pointer",
|
|
1673
|
-
padding
|
|
1674
|
-
|
|
1675
|
-
// CTA); 8px only when the page has no template button to match.
|
|
1676
|
-
borderRadius: ctx.buttonRadius ?? 8,
|
|
2374
|
+
// Shape/padding/typography follow the host template's own buttons.
|
|
2375
|
+
...buttonShellStyle(ctx),
|
|
1677
2376
|
// Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
|
|
1678
2377
|
// reads correctly on custom palettes.
|
|
1679
2378
|
background: ctx.brand.palette.primary,
|
|
1680
|
-
color: ctx.buttonLabel ?? ctx.brand
|
|
1681
|
-
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
2379
|
+
color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
|
|
1682
2380
|
},
|
|
1683
2381
|
children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
|
|
1684
2382
|
},
|
|
@@ -1713,7 +2411,7 @@ function renderNode(node, ctx, path) {
|
|
|
1713
2411
|
function AiTreeRenderer({
|
|
1714
2412
|
tree,
|
|
1715
2413
|
brand,
|
|
1716
|
-
|
|
2414
|
+
buttonStyle,
|
|
1717
2415
|
resolveMedia,
|
|
1718
2416
|
editKeyPrefix
|
|
1719
2417
|
}) {
|
|
@@ -1723,13 +2421,18 @@ function AiTreeRenderer({
|
|
|
1723
2421
|
const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
|
|
1724
2422
|
const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
|
|
1725
2423
|
const blockBrand = band?.brand ?? resolvedBrand;
|
|
2424
|
+
const placeholderMap = buildPlaceholderMap(tree);
|
|
1726
2425
|
const ctx = {
|
|
1727
2426
|
brand: blockBrand,
|
|
1728
|
-
|
|
2427
|
+
// An owner/library ref resolves through the host resolver; a `placeholder:<subject>` ref the
|
|
2428
|
+
// host cannot resolve falls back to real stock photography (the per-section map first, then a
|
|
2429
|
+
// standalone resolve), so generated galleries, image rows, and overlay backgrounds arrive with
|
|
2430
|
+
// photos instead of grey boxes.
|
|
2431
|
+
resolveMedia: (ref) => resolveMedia?.(ref) ?? placeholderMap[ref] ?? resolvePlaceholderRef(ref),
|
|
1729
2432
|
cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
|
|
1730
2433
|
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
|
|
1731
2434
|
sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
|
|
1732
|
-
|
|
2435
|
+
buttonStyle,
|
|
1733
2436
|
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
1734
2437
|
};
|
|
1735
2438
|
const settings = tree.settings ?? {};
|
|
@@ -1752,11 +2455,25 @@ function AiTreeRenderer({
|
|
|
1752
2455
|
}
|
|
1753
2456
|
})();
|
|
1754
2457
|
const distributed = !isOverlay && settings.textDistribution;
|
|
2458
|
+
const rowAlignItems = (rowAlign) => {
|
|
2459
|
+
if (rowAlign === "top") return "start";
|
|
2460
|
+
if (rowAlign === "bottom") return "end";
|
|
2461
|
+
if (rowAlign === "center" || rowAlign === "stretch") return rowAlign;
|
|
2462
|
+
if (distributed === "space-between") return "stretch";
|
|
2463
|
+
return (distributed ?? settings.verticalPosition) === "top" ? "start" : "center";
|
|
2464
|
+
};
|
|
2465
|
+
const cellAlignStyle = (blockAlign) => blockAlign ? {
|
|
2466
|
+
display: "flex",
|
|
2467
|
+
flexDirection: "column",
|
|
2468
|
+
alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
|
|
2469
|
+
textAlign: blockAlign
|
|
2470
|
+
} : {};
|
|
1755
2471
|
return /* @__PURE__ */ jsxs(
|
|
1756
2472
|
"section",
|
|
1757
2473
|
{
|
|
1758
2474
|
"data-ai-section": tree.tag ?? "",
|
|
1759
2475
|
...bgAttrs,
|
|
2476
|
+
"data-ai-responsive": "",
|
|
1760
2477
|
style: {
|
|
1761
2478
|
position: "relative",
|
|
1762
2479
|
padding: `${pad}px 0`,
|
|
@@ -1767,12 +2484,13 @@ function AiTreeRenderer({
|
|
|
1767
2484
|
color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
|
|
1768
2485
|
},
|
|
1769
2486
|
children: [
|
|
1770
|
-
|
|
2487
|
+
/* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
|
|
1771
2488
|
/* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
|
|
2489
|
+
isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
|
|
1772
2490
|
/* @__PURE__ */ jsx(
|
|
1773
2491
|
"div",
|
|
1774
2492
|
{
|
|
1775
|
-
"data-ai-
|
|
2493
|
+
"data-ai-section-inner": "",
|
|
1776
2494
|
style: {
|
|
1777
2495
|
position: "relative",
|
|
1778
2496
|
maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
|
|
@@ -1783,12 +2501,12 @@ function AiTreeRenderer({
|
|
|
1783
2501
|
children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
|
|
1784
2502
|
"div",
|
|
1785
2503
|
{
|
|
1786
|
-
"data-ai-
|
|
2504
|
+
"data-ai-columns": "",
|
|
1787
2505
|
style: {
|
|
1788
2506
|
display: "grid",
|
|
1789
2507
|
gridTemplateColumns: "repeat(12, 1fr)",
|
|
1790
2508
|
gap: AI_TREE_TOKENS.spacing6,
|
|
1791
|
-
alignItems:
|
|
2509
|
+
alignItems: rowAlignItems(row.align),
|
|
1792
2510
|
marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
|
|
1793
2511
|
},
|
|
1794
2512
|
children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
|
|
@@ -1798,6 +2516,8 @@ function AiTreeRenderer({
|
|
|
1798
2516
|
style: {
|
|
1799
2517
|
gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
|
|
1800
2518
|
minWidth: 0,
|
|
2519
|
+
// Horizontal placement of the block's content within its column.
|
|
2520
|
+
...cellAlignStyle(block.align),
|
|
1801
2521
|
// space-between: each column becomes a flex column whose content spreads over
|
|
1802
2522
|
// the full row height instead of clumping at the top.
|
|
1803
2523
|
...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
|
|
@@ -1820,7 +2540,7 @@ function AiTreeRenderer({
|
|
|
1820
2540
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1821
2541
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
1822
2542
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
1823
|
-
var
|
|
2543
|
+
var REMOVED_ATTR2 = "data-ohw-ai-removed";
|
|
1824
2544
|
var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
|
|
1825
2545
|
var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
1826
2546
|
function readRootVar(name) {
|
|
@@ -1844,13 +2564,13 @@ function deriveBrandOverride() {
|
|
|
1844
2564
|
};
|
|
1845
2565
|
}
|
|
1846
2566
|
function deriveTemplateBrand() {
|
|
1847
|
-
const
|
|
1848
|
-
const
|
|
1849
|
-
const light = readRootVar("--color-light");
|
|
2567
|
+
const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
|
|
2568
|
+
const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
|
|
2569
|
+
const light = readRootVar("--brand-background") || readRootVar("--color-light");
|
|
1850
2570
|
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");
|
|
2571
|
+
const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
|
|
2572
|
+
const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
|
|
2573
|
+
const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
|
|
1854
2574
|
return {
|
|
1855
2575
|
palette: { dark, primary, accent: accent || dark, light },
|
|
1856
2576
|
fonts: {
|
|
@@ -1859,12 +2579,32 @@ function deriveTemplateBrand() {
|
|
|
1859
2579
|
}
|
|
1860
2580
|
};
|
|
1861
2581
|
}
|
|
1862
|
-
function
|
|
2582
|
+
function deriveTemplateButtonStyle() {
|
|
1863
2583
|
if (typeof document === "undefined") return null;
|
|
1864
|
-
const btn = document.
|
|
2584
|
+
const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
|
|
2585
|
+
(el) => !el.closest(`[${CONTAINER_ATTR}]`)
|
|
2586
|
+
);
|
|
1865
2587
|
if (!btn) return null;
|
|
1866
|
-
const
|
|
1867
|
-
|
|
2588
|
+
const cs = getComputedStyle(btn);
|
|
2589
|
+
const corners = [
|
|
2590
|
+
cs.borderTopLeftRadius,
|
|
2591
|
+
cs.borderTopRightRadius,
|
|
2592
|
+
cs.borderBottomRightRadius,
|
|
2593
|
+
cs.borderBottomLeftRadius
|
|
2594
|
+
].map((v) => v || "0px");
|
|
2595
|
+
const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
|
|
2596
|
+
const px = (v) => parseFloat(v) || 0;
|
|
2597
|
+
const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom));
|
|
2598
|
+
const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
|
|
2599
|
+
return {
|
|
2600
|
+
radius: radius || "10px",
|
|
2601
|
+
padding: `${padY}px ${padX}px`,
|
|
2602
|
+
fontFamily: cs.fontFamily || "",
|
|
2603
|
+
fontSize: cs.fontSize || "",
|
|
2604
|
+
fontWeight: cs.fontWeight || "",
|
|
2605
|
+
letterSpacing: cs.letterSpacing || "",
|
|
2606
|
+
textTransform: cs.textTransform || ""
|
|
2607
|
+
};
|
|
1868
2608
|
}
|
|
1869
2609
|
var mounted = /* @__PURE__ */ new Map();
|
|
1870
2610
|
function findTemplateSection(id) {
|
|
@@ -1914,18 +2654,18 @@ function placeContainer(container, entry) {
|
|
|
1914
2654
|
}
|
|
1915
2655
|
function syncRemovedSections(state) {
|
|
1916
2656
|
const removed = new Set(state.removed ?? []);
|
|
1917
|
-
for (const el of document.querySelectorAll(`[${
|
|
2657
|
+
for (const el of document.querySelectorAll(`[${REMOVED_ATTR2}]`)) {
|
|
1918
2658
|
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
1919
2659
|
if (!removed.has(id)) {
|
|
1920
2660
|
el.style.removeProperty("display");
|
|
1921
|
-
el.removeAttribute(
|
|
2661
|
+
el.removeAttribute(REMOVED_ATTR2);
|
|
1922
2662
|
}
|
|
1923
2663
|
}
|
|
1924
2664
|
for (const id of removed) {
|
|
1925
2665
|
const section = findTemplateSection(id);
|
|
1926
2666
|
if (section && !section.hasAttribute(REPLACED_ATTR)) {
|
|
1927
2667
|
section.style.display = "none";
|
|
1928
|
-
section.setAttribute(
|
|
2668
|
+
section.setAttribute(REMOVED_ATTR2, "");
|
|
1929
2669
|
}
|
|
1930
2670
|
}
|
|
1931
2671
|
}
|
|
@@ -1942,7 +2682,7 @@ function syncTemplateHidden(state, pageHasSections) {
|
|
|
1942
2682
|
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
1943
2683
|
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
|
|
1944
2684
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
1945
|
-
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(
|
|
2685
|
+
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR2)) continue;
|
|
1946
2686
|
el.style.display = "none";
|
|
1947
2687
|
el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
|
|
1948
2688
|
}
|
|
@@ -1966,18 +2706,23 @@ function syncReplacedOriginals(state) {
|
|
|
1966
2706
|
}
|
|
1967
2707
|
}
|
|
1968
2708
|
var sectionOrderIndex = /* @__PURE__ */ new Map();
|
|
2709
|
+
var removedSectionIds = /* @__PURE__ */ new Set();
|
|
1969
2710
|
function setAiSectionOrder(raw, currentPath) {
|
|
1970
2711
|
const next = /* @__PURE__ */ new Map();
|
|
2712
|
+
const removed = /* @__PURE__ */ new Set();
|
|
1971
2713
|
if (raw) {
|
|
1972
2714
|
try {
|
|
1973
2715
|
const entries = JSON.parse(raw);
|
|
1974
2716
|
for (const entry of entries) {
|
|
1975
|
-
if (
|
|
2717
|
+
if (entry.pagePath && entry.pagePath !== currentPath) continue;
|
|
2718
|
+
next.set(entry.instanceId, entry.order);
|
|
2719
|
+
if (entry.removed) removed.add(entry.instanceId);
|
|
1976
2720
|
}
|
|
1977
2721
|
} catch {
|
|
1978
2722
|
}
|
|
1979
2723
|
}
|
|
1980
2724
|
sectionOrderIndex = next;
|
|
2725
|
+
removedSectionIds = removed;
|
|
1981
2726
|
}
|
|
1982
2727
|
function applyExplicitOrder(entries) {
|
|
1983
2728
|
if (sectionOrderIndex.size === 0) return entries;
|
|
@@ -2013,11 +2758,23 @@ function orderByChain(sections) {
|
|
|
2013
2758
|
for (const root of roots) visit(root);
|
|
2014
2759
|
return out.length === sections.length ? out : sections;
|
|
2015
2760
|
}
|
|
2761
|
+
function syncSoftRemovedGenerated() {
|
|
2762
|
+
for (const [id, section] of mounted) {
|
|
2763
|
+
const el = section.container;
|
|
2764
|
+
if (removedSectionIds.has(id)) {
|
|
2765
|
+
el.style.display = "none";
|
|
2766
|
+
el.setAttribute(REMOVED_ATTR, "");
|
|
2767
|
+
} else if (el.hasAttribute(REMOVED_ATTR)) {
|
|
2768
|
+
el.style.removeProperty("display");
|
|
2769
|
+
el.removeAttribute(REMOVED_ATTR);
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2016
2773
|
function applyAiSectionsToDom(state, options) {
|
|
2017
2774
|
if (typeof document === "undefined") return;
|
|
2018
2775
|
const brandOverride = deriveBrandOverride();
|
|
2019
2776
|
const templateBrand = deriveTemplateBrand();
|
|
2020
|
-
const
|
|
2777
|
+
const templateButtonStyle = deriveTemplateButtonStyle();
|
|
2021
2778
|
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2022
2779
|
const pagePath = window.location.pathname;
|
|
2023
2780
|
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
@@ -2057,7 +2814,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2057
2814
|
{
|
|
2058
2815
|
tree: entry.tree,
|
|
2059
2816
|
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2060
|
-
|
|
2817
|
+
buttonStyle: templateButtonStyle,
|
|
2061
2818
|
resolveMedia,
|
|
2062
2819
|
editKeyPrefix: `ai.${entry.id}`
|
|
2063
2820
|
}
|
|
@@ -2080,6 +2837,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2080
2837
|
syncReplacedOriginals(state);
|
|
2081
2838
|
syncRemovedSections(state);
|
|
2082
2839
|
syncTemplateHidden(state, pageSections.length > 0);
|
|
2840
|
+
syncSoftRemovedGenerated();
|
|
2083
2841
|
}
|
|
2084
2842
|
|
|
2085
2843
|
// src/useLinkHrefGuardian.ts
|
|
@@ -7809,6 +8567,7 @@ function MediaOverlay({
|
|
|
7809
8567
|
(prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
|
|
7810
8568
|
);
|
|
7811
8569
|
}, [isVideo]);
|
|
8570
|
+
const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
|
|
7812
8571
|
const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
|
|
7813
8572
|
const box = {
|
|
7814
8573
|
position: "fixed",
|
|
@@ -7938,17 +8697,17 @@ function MediaOverlay({
|
|
|
7938
8697
|
},
|
|
7939
8698
|
children: [
|
|
7940
8699
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7941
|
-
|
|
8700
|
+
replaceLabel
|
|
7942
8701
|
]
|
|
7943
8702
|
}
|
|
7944
8703
|
),
|
|
7945
|
-
replaceMode
|
|
8704
|
+
showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
|
|
7946
8705
|
Button,
|
|
7947
8706
|
{
|
|
7948
8707
|
"data-ohw-media-overlay": "",
|
|
7949
8708
|
variant: "outline",
|
|
7950
8709
|
size: "sm",
|
|
7951
|
-
"aria-label":
|
|
8710
|
+
"aria-label": replaceLabel,
|
|
7952
8711
|
className: "gap-1.5 cursor-pointer hover:bg-background",
|
|
7953
8712
|
style: {
|
|
7954
8713
|
...OVERLAY_BUTTON_STYLE,
|
|
@@ -7971,7 +8730,7 @@ function MediaOverlay({
|
|
|
7971
8730
|
},
|
|
7972
8731
|
children: [
|
|
7973
8732
|
isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
|
|
7974
|
-
replaceMode === "full" ?
|
|
8733
|
+
replaceMode === "full" ? replaceLabel : null
|
|
7975
8734
|
]
|
|
7976
8735
|
}
|
|
7977
8736
|
)
|
|
@@ -8012,215 +8771,33 @@ function CarouselOverlay({
|
|
|
8012
8771
|
boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
|
|
8013
8772
|
background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
|
|
8014
8773
|
},
|
|
8015
|
-
onClick: () => onEdit(hover.key),
|
|
8016
|
-
children: /* @__PURE__ */ jsxs8(
|
|
8017
|
-
Button,
|
|
8018
|
-
{
|
|
8019
|
-
"data-ohw-carousel-overlay": "",
|
|
8020
|
-
variant: "outline",
|
|
8021
|
-
size: "sm",
|
|
8022
|
-
className: "cursor-pointer gap-1.5 hover:bg-background",
|
|
8023
|
-
style: OVERLAY_BUTTON_STYLE2,
|
|
8024
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
8025
|
-
onClick: (e) => {
|
|
8026
|
-
e.stopPropagation();
|
|
8027
|
-
onEdit(hover.key);
|
|
8028
|
-
},
|
|
8029
|
-
children: [
|
|
8030
|
-
/* @__PURE__ */ jsx16(GalleryHorizontal, { size: 14 }),
|
|
8031
|
-
"Edit gallery"
|
|
8032
|
-
]
|
|
8033
|
-
}
|
|
8034
|
-
)
|
|
8035
|
-
}
|
|
8036
|
-
);
|
|
8037
|
-
}
|
|
8038
|
-
|
|
8039
|
-
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
8040
|
-
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
|
|
8041
|
-
import { Check, X } from "lucide-react";
|
|
8042
|
-
|
|
8043
|
-
// src/lib/sections.ts
|
|
8044
|
-
var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
8045
|
-
function isChromeSection(el) {
|
|
8046
|
-
return el.matches("header, nav, footer, aside");
|
|
8047
|
-
}
|
|
8048
|
-
function titleCaseSectionId(id) {
|
|
8049
|
-
return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
8050
|
-
}
|
|
8051
|
-
function parseSectionsFromRoot(root) {
|
|
8052
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8053
|
-
const sections = [];
|
|
8054
|
-
for (const el of root.querySelectorAll("[data-ohw-section]")) {
|
|
8055
|
-
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
8056
|
-
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
8057
|
-
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
8058
|
-
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
8059
|
-
continue;
|
|
8060
|
-
seen.add(id);
|
|
8061
|
-
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
8062
|
-
sections.push({ id, label });
|
|
8063
|
-
}
|
|
8064
|
-
return sections;
|
|
8065
|
-
}
|
|
8066
|
-
function collectSectionsFromDom() {
|
|
8067
|
-
if (typeof document === "undefined") return [];
|
|
8068
|
-
return parseSectionsFromRoot(document);
|
|
8069
|
-
}
|
|
8070
|
-
function parseSectionsFromHtml(html) {
|
|
8071
|
-
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
8072
|
-
return parseSectionsFromRoot(doc);
|
|
8073
|
-
}
|
|
8074
|
-
|
|
8075
|
-
// src/lib/section-instances.ts
|
|
8076
|
-
var SECTION_ORDER_KEY = "__ohw_section_order";
|
|
8077
|
-
var REMOVED_ATTR2 = "data-ohw-section-removed";
|
|
8078
|
-
function isRemovedSection(el) {
|
|
8079
|
-
return el.hasAttribute(REMOVED_ATTR2);
|
|
8080
|
-
}
|
|
8081
|
-
function topLevelSections() {
|
|
8082
|
-
return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
8083
|
-
(el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isRemovedSection(el)
|
|
8084
|
-
);
|
|
8085
|
-
}
|
|
8086
|
-
function instanceIdOf(el) {
|
|
8087
|
-
return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
|
|
8088
|
-
}
|
|
8089
|
-
function findByInstanceId(instanceId) {
|
|
8090
|
-
const escapedId = CSS.escape(instanceId);
|
|
8091
|
-
return document.querySelector(`[data-ohw-instance="${escapedId}"]`) ?? document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
|
|
8092
|
-
}
|
|
8093
|
-
function planSectionMove(instanceId, targetIndex, currentPath) {
|
|
8094
|
-
const sections = topLevelSections();
|
|
8095
|
-
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
8096
|
-
if (index === -1) return null;
|
|
8097
|
-
const dragged = sections[index];
|
|
8098
|
-
const others = sections.filter((_, i) => i !== index);
|
|
8099
|
-
const clamped = Math.max(0, Math.min(targetIndex, others.length));
|
|
8100
|
-
const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
|
|
8101
|
-
return reordered.map((el, order) => ({
|
|
8102
|
-
instanceId: instanceIdOf(el),
|
|
8103
|
-
type: el.getAttribute("data-ohw-section") ?? "",
|
|
8104
|
-
order,
|
|
8105
|
-
pagePath: currentPath
|
|
8106
|
-
}));
|
|
8107
|
-
}
|
|
8108
|
-
function moveSectionInstance(instanceId, direction, currentPath) {
|
|
8109
|
-
const sections = topLevelSections();
|
|
8110
|
-
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
8111
|
-
if (index === -1) return null;
|
|
8112
|
-
const siblingIndex = direction === "up" ? index - 1 : index + 1;
|
|
8113
|
-
if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
|
|
8114
|
-
const entries = planSectionMove(instanceId, siblingIndex, currentPath);
|
|
8115
|
-
if (!entries) return null;
|
|
8116
|
-
applyPersistedOrder(entries);
|
|
8117
|
-
return entries;
|
|
8118
|
-
}
|
|
8119
|
-
function syncRemovedFlags(entries) {
|
|
8120
|
-
const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
|
|
8121
|
-
document.querySelectorAll(`[${REMOVED_ATTR2}]`).forEach((el) => {
|
|
8122
|
-
if (!removedIds.has(instanceIdOf(el))) {
|
|
8123
|
-
el.style.removeProperty("display");
|
|
8124
|
-
el.removeAttribute(REMOVED_ATTR2);
|
|
8125
|
-
}
|
|
8126
|
-
});
|
|
8127
|
-
for (const id of removedIds) {
|
|
8128
|
-
const el = findByInstanceId(id);
|
|
8129
|
-
if (el) {
|
|
8130
|
-
el.style.display = "none";
|
|
8131
|
-
el.setAttribute(REMOVED_ATTR2, "");
|
|
8774
|
+
onClick: () => onEdit(hover.key),
|
|
8775
|
+
children: /* @__PURE__ */ jsxs8(
|
|
8776
|
+
Button,
|
|
8777
|
+
{
|
|
8778
|
+
"data-ohw-carousel-overlay": "",
|
|
8779
|
+
variant: "outline",
|
|
8780
|
+
size: "sm",
|
|
8781
|
+
className: "cursor-pointer gap-1.5 hover:bg-background",
|
|
8782
|
+
style: OVERLAY_BUTTON_STYLE2,
|
|
8783
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
8784
|
+
onClick: (e) => {
|
|
8785
|
+
e.stopPropagation();
|
|
8786
|
+
onEdit(hover.key);
|
|
8787
|
+
},
|
|
8788
|
+
children: [
|
|
8789
|
+
/* @__PURE__ */ jsx16(GalleryHorizontal, { size: 14 }),
|
|
8790
|
+
"Edit gallery"
|
|
8791
|
+
]
|
|
8792
|
+
}
|
|
8793
|
+
)
|
|
8132
8794
|
}
|
|
8133
|
-
}
|
|
8134
|
-
}
|
|
8135
|
-
function applyPersistedOrder(entries) {
|
|
8136
|
-
syncRemovedFlags(entries);
|
|
8137
|
-
if (entries.length === 0) return;
|
|
8138
|
-
const sections = topLevelSections();
|
|
8139
|
-
if (sections.length === 0) return;
|
|
8140
|
-
const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
|
|
8141
|
-
const ordered = [...sections].sort((a, b) => {
|
|
8142
|
-
const aOrder = orderIndex.get(instanceIdOf(a));
|
|
8143
|
-
const bOrder = orderIndex.get(instanceIdOf(b));
|
|
8144
|
-
if (aOrder === void 0 && bOrder === void 0) return 0;
|
|
8145
|
-
if (aOrder === void 0) return 1;
|
|
8146
|
-
if (bOrder === void 0) return -1;
|
|
8147
|
-
return aOrder - bOrder;
|
|
8148
|
-
});
|
|
8149
|
-
let prev = null;
|
|
8150
|
-
for (const el of ordered) {
|
|
8151
|
-
if (prev) prev.after(el);
|
|
8152
|
-
prev = el;
|
|
8153
|
-
}
|
|
8154
|
-
}
|
|
8155
|
-
function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
|
|
8156
|
-
if (!findByInstanceId(instanceId)) return null;
|
|
8157
|
-
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
8158
|
-
const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
|
|
8159
|
-
(el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
|
|
8160
8795
|
);
|
|
8161
|
-
allSections.forEach((el, order) => {
|
|
8162
|
-
const id = instanceIdOf(el);
|
|
8163
|
-
if (!byId.has(id)) {
|
|
8164
|
-
byId.set(id, { instanceId: id, type: el.getAttribute("data-ohw-section") ?? "", order, pagePath: currentPath });
|
|
8165
|
-
}
|
|
8166
|
-
});
|
|
8167
|
-
const target = byId.get(instanceId);
|
|
8168
|
-
if (!target) return null;
|
|
8169
|
-
byId.set(instanceId, { ...target, removed });
|
|
8170
|
-
const entries = Array.from(byId.values());
|
|
8171
|
-
applyPersistedOrder(entries);
|
|
8172
|
-
return entries;
|
|
8173
|
-
}
|
|
8174
|
-
function deleteSectionInstance(instanceId, currentPath, existingEntries) {
|
|
8175
|
-
return setSectionRemoved(instanceId, currentPath, existingEntries, true);
|
|
8176
|
-
}
|
|
8177
|
-
function restoreSectionInstance(instanceId, currentPath, existingEntries) {
|
|
8178
|
-
return setSectionRemoved(instanceId, currentPath, existingEntries, false);
|
|
8179
|
-
}
|
|
8180
|
-
function newInstanceId() {
|
|
8181
|
-
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
8182
|
-
}
|
|
8183
|
-
function getPageSectionOrderEntries(raw, currentPath) {
|
|
8184
|
-
if (!raw) return [];
|
|
8185
|
-
try {
|
|
8186
|
-
const entries = JSON.parse(raw);
|
|
8187
|
-
return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
|
|
8188
|
-
} catch {
|
|
8189
|
-
return [];
|
|
8190
|
-
}
|
|
8191
|
-
}
|
|
8192
|
-
function rekeySectionSubtree(root, instanceId) {
|
|
8193
|
-
const suffix = `::${instanceId}`;
|
|
8194
|
-
const rekey = (el, attr) => {
|
|
8195
|
-
const current = el.getAttribute(attr);
|
|
8196
|
-
if (current) el.setAttribute(attr, `${current}${suffix}`);
|
|
8197
|
-
};
|
|
8198
|
-
if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
|
|
8199
|
-
if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
|
|
8200
|
-
root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
|
|
8201
|
-
root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
|
|
8202
|
-
}
|
|
8203
|
-
function initSectionInstancesFromContent(content, currentPath) {
|
|
8204
|
-
document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
|
|
8205
|
-
el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
|
|
8206
|
-
});
|
|
8207
|
-
const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
|
|
8208
|
-
for (const entry of entries) {
|
|
8209
|
-
if (entry.instanceId === entry.type) continue;
|
|
8210
|
-
if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
|
|
8211
|
-
const original = document.querySelector(
|
|
8212
|
-
`[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
|
|
8213
|
-
);
|
|
8214
|
-
if (!original) continue;
|
|
8215
|
-
const clone = original.cloneNode(true);
|
|
8216
|
-
clone.setAttribute("data-ohw-instance", entry.instanceId);
|
|
8217
|
-
rekeySectionSubtree(clone, entry.instanceId);
|
|
8218
|
-
original.insertAdjacentElement("afterend", clone);
|
|
8219
|
-
}
|
|
8220
|
-
applyPersistedOrder(entries);
|
|
8221
8796
|
}
|
|
8222
8797
|
|
|
8223
8798
|
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
8799
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
|
|
8800
|
+
import { Check, X } from "lucide-react";
|
|
8224
8801
|
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
8225
8802
|
function findSectionElement(instanceId) {
|
|
8226
8803
|
const escaped = CSS.escape(instanceId);
|
|
@@ -11123,7 +11700,6 @@ function iconMarkupSizedFor(slot, markup) {
|
|
|
11123
11700
|
holder.innerHTML = markup;
|
|
11124
11701
|
const glyph = holder.querySelector(GLYPH_SELECTOR);
|
|
11125
11702
|
if (!glyph) return markup;
|
|
11126
|
-
glyph.style.display = "block";
|
|
11127
11703
|
if (!box) {
|
|
11128
11704
|
glyph.style.width = "1.5em";
|
|
11129
11705
|
glyph.style.height = "1.5em";
|
|
@@ -12937,6 +13513,7 @@ function readLogoSizeState(content, placement) {
|
|
|
12937
13513
|
function getLogoElement(el) {
|
|
12938
13514
|
const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
|
|
12939
13515
|
if (marked) return marked;
|
|
13516
|
+
if (el.closest('[data-ohw-editable="icon"]')) return null;
|
|
12940
13517
|
const root = el.closest("nav, [data-ohw-nav-root], footer");
|
|
12941
13518
|
if (!root) return null;
|
|
12942
13519
|
const anchor = el.closest("a");
|
|
@@ -14009,15 +14586,17 @@ function useSectionDrag({
|
|
|
14009
14586
|
clearSectionDragVisuals();
|
|
14010
14587
|
return;
|
|
14011
14588
|
}
|
|
14012
|
-
const orderJson = JSON.stringify(
|
|
14589
|
+
const orderJson = JSON.stringify(
|
|
14590
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
14591
|
+
);
|
|
14013
14592
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
14014
14593
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
14015
14594
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
14016
|
-
applyPersistedOrder(
|
|
14595
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
14017
14596
|
clearSectionDragVisuals();
|
|
14018
14597
|
requestAnimationFrame(() => {
|
|
14019
14598
|
if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
|
|
14020
|
-
applyPersistedOrder(
|
|
14599
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
14021
14600
|
}
|
|
14022
14601
|
requestAnimationFrame(() => {
|
|
14023
14602
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -14318,6 +14897,9 @@ function collectEditableNodes(extraContent, root = document) {
|
|
|
14318
14897
|
if (el.dataset.ohwEditable === "link") {
|
|
14319
14898
|
return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref3(el) };
|
|
14320
14899
|
}
|
|
14900
|
+
if (el.dataset.ohwEditable === "map") {
|
|
14901
|
+
return { key: el.dataset.ohwKey ?? "", type: "map", text: el.dataset.ohwMapQuery ?? "" };
|
|
14902
|
+
}
|
|
14321
14903
|
return {
|
|
14322
14904
|
key: el.dataset.ohwKey ?? "",
|
|
14323
14905
|
type: el.dataset.ohwEditable ?? "text",
|
|
@@ -14871,21 +15453,10 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
14871
15453
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
14872
15454
|
};
|
|
14873
15455
|
}
|
|
14874
|
-
function
|
|
14875
|
-
|
|
14876
|
-
const
|
|
14877
|
-
|
|
14878
|
-
return { effectiveInsertAfter, insertBefore };
|
|
14879
|
-
}
|
|
14880
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
14881
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
14882
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
14883
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
14884
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
14885
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
14886
|
-
}
|
|
14887
|
-
if (!anchorEl) return null;
|
|
14888
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
15456
|
+
function resolveEntryAnchor(entry) {
|
|
15457
|
+
if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
|
|
15458
|
+
const parsed = parseSchedulingInsertAfter(entry.insertAfter);
|
|
15459
|
+
return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
|
|
14889
15460
|
}
|
|
14890
15461
|
function schedulingMountDepth(insertAfter) {
|
|
14891
15462
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -14902,8 +15473,7 @@ function getPageSchedulingEntries(raw) {
|
|
|
14902
15473
|
}
|
|
14903
15474
|
}
|
|
14904
15475
|
function isSchedulingWidgetMissing(entry) {
|
|
14905
|
-
|
|
14906
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
15476
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
|
|
14907
15477
|
}
|
|
14908
15478
|
function hasMissingSchedulingWidgets(entries) {
|
|
14909
15479
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -14933,16 +15503,17 @@ function initSectionsFromContent(content, removeExisting = false) {
|
|
|
14933
15503
|
} catch {
|
|
14934
15504
|
}
|
|
14935
15505
|
}
|
|
14936
|
-
function mountSchedulingWidget(
|
|
14937
|
-
const
|
|
14938
|
-
const sectionId = schedulingSectionId(
|
|
15506
|
+
function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
|
|
15507
|
+
const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
|
|
15508
|
+
const sectionId = schedulingSectionId(widgetId);
|
|
14939
15509
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
14940
|
-
const
|
|
14941
|
-
if (!
|
|
15510
|
+
const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
|
|
15511
|
+
if (!anchorEl) return false;
|
|
15512
|
+
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
14942
15513
|
const container = document.createElement("div");
|
|
14943
15514
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
14944
|
-
if (
|
|
14945
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
15515
|
+
if (beforeId) {
|
|
15516
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
|
|
14946
15517
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
14947
15518
|
if (!beforePoint) return false;
|
|
14948
15519
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -14953,19 +15524,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14953
15524
|
}
|
|
14954
15525
|
tail.insertAdjacentElement("afterend", container);
|
|
14955
15526
|
}
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
14961
|
-
|
|
14962
|
-
|
|
14963
|
-
|
|
14964
|
-
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
15527
|
+
try {
|
|
15528
|
+
const root = createRoot2(container);
|
|
15529
|
+
flushSync2(() => {
|
|
15530
|
+
root.render(
|
|
15531
|
+
/* @__PURE__ */ jsx33(
|
|
15532
|
+
SchedulingWidget,
|
|
15533
|
+
{
|
|
15534
|
+
notifyOnConnect,
|
|
15535
|
+
initialScheduleId: scheduleId,
|
|
15536
|
+
insertAfter: widgetId
|
|
15537
|
+
}
|
|
15538
|
+
)
|
|
15539
|
+
);
|
|
15540
|
+
});
|
|
15541
|
+
} catch (err) {
|
|
15542
|
+
console.error("[ow:scheduling] render threw", err);
|
|
15543
|
+
container.remove();
|
|
15544
|
+
return false;
|
|
15545
|
+
}
|
|
14969
15546
|
const tracker = getSectionsTracker();
|
|
14970
15547
|
let sections = [];
|
|
14971
15548
|
try {
|
|
@@ -14973,10 +15550,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
14973
15550
|
} catch {
|
|
14974
15551
|
}
|
|
14975
15552
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
14976
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
15553
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
|
|
14977
15554
|
sections.push({
|
|
14978
15555
|
type: "scheduling",
|
|
14979
|
-
insertAfter:
|
|
15556
|
+
insertAfter: widgetId,
|
|
15557
|
+
anchorId,
|
|
15558
|
+
beforeId: beforeId ?? null,
|
|
14980
15559
|
pagePath: window.location.pathname,
|
|
14981
15560
|
...scheduleId ? { scheduleId } : {}
|
|
14982
15561
|
});
|
|
@@ -14990,7 +15569,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
14990
15569
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
14991
15570
|
const entry = pending[i];
|
|
14992
15571
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
14993
|
-
|
|
15572
|
+
const { anchorId, beforeId } = resolveEntryAnchor(entry);
|
|
15573
|
+
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
14994
15574
|
pending.splice(i, 1);
|
|
14995
15575
|
}
|
|
14996
15576
|
}
|
|
@@ -15082,7 +15662,7 @@ var EDITOR_CHROME_SELECTOR = '[data-ohw-toolbar], [data-ohw-form-toolbar], [data
|
|
|
15082
15662
|
function isOverEditorChrome(x, y) {
|
|
15083
15663
|
return document.elementsFromPoint(x, y).some((el) => el instanceof HTMLElement && el.matches(EDITOR_CHROME_SELECTOR));
|
|
15084
15664
|
}
|
|
15085
|
-
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"])';
|
|
15665
|
+
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"])';
|
|
15086
15666
|
function getVideoEl2(el) {
|
|
15087
15667
|
return el instanceof HTMLVideoElement ? el : el.querySelector("video");
|
|
15088
15668
|
}
|
|
@@ -15138,6 +15718,12 @@ function applyVideoSettingNode(key, val) {
|
|
|
15138
15718
|
});
|
|
15139
15719
|
return true;
|
|
15140
15720
|
}
|
|
15721
|
+
function applyMapQuery(el, val) {
|
|
15722
|
+
if (!(el instanceof HTMLIFrameElement)) return;
|
|
15723
|
+
const nextSrc = `https://www.google.com/maps?q=${encodeURIComponent(val)}&output=embed`;
|
|
15724
|
+
if (el.src !== nextSrc) el.src = nextSrc;
|
|
15725
|
+
el.setAttribute("data-ohw-map-query", val);
|
|
15726
|
+
}
|
|
15141
15727
|
function applyLinkByKey(key, val) {
|
|
15142
15728
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
15143
15729
|
if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
|
|
@@ -15148,6 +15734,11 @@ function applyLinkByKey(key, val) {
|
|
|
15148
15734
|
hrefAnchors.forEach((el) => applyLinkHref(el, val));
|
|
15149
15735
|
}
|
|
15150
15736
|
}
|
|
15737
|
+
function isInsideLinkEditor(target) {
|
|
15738
|
+
return Boolean(
|
|
15739
|
+
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"]')
|
|
15740
|
+
);
|
|
15741
|
+
}
|
|
15151
15742
|
function isInsideFloatingPanel(target) {
|
|
15152
15743
|
return Boolean(target.closest("[data-ohw-floating-panel]"));
|
|
15153
15744
|
}
|
|
@@ -15155,11 +15746,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
|
|
|
15155
15746
|
const el = document.elementFromPoint(clientX, clientY);
|
|
15156
15747
|
return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
|
|
15157
15748
|
}
|
|
15158
|
-
function isInsideLinkEditor(target) {
|
|
15159
|
-
return Boolean(
|
|
15160
|
-
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"]')
|
|
15161
|
-
);
|
|
15162
|
-
}
|
|
15163
15749
|
function getHrefKeyFromElement(el) {
|
|
15164
15750
|
if (!el) return null;
|
|
15165
15751
|
const anchor = el.closest("[data-ohw-href-key]");
|
|
@@ -15418,7 +16004,7 @@ function getNavigationSelectionParent(el) {
|
|
|
15418
16004
|
if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
|
|
15419
16005
|
return getFooterLinksContainer();
|
|
15420
16006
|
}
|
|
15421
|
-
if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
|
|
16007
|
+
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)) {
|
|
15422
16008
|
return getNavigationRoot(el);
|
|
15423
16009
|
}
|
|
15424
16010
|
return null;
|
|
@@ -15633,7 +16219,6 @@ var ICONS = {
|
|
|
15633
16219
|
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"/>',
|
|
15634
16220
|
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"/>'
|
|
15635
16221
|
};
|
|
15636
|
-
var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
|
|
15637
16222
|
var SELECTION_CHROME_GAP2 = 4;
|
|
15638
16223
|
var TOOLBAR_STROKE_GAP2 = 4;
|
|
15639
16224
|
var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
|
|
@@ -16013,6 +16598,7 @@ function StateToggle({
|
|
|
16013
16598
|
);
|
|
16014
16599
|
}
|
|
16015
16600
|
var contentCache = /* @__PURE__ */ new Map();
|
|
16601
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
16016
16602
|
var brandingCache = /* @__PURE__ */ new Map();
|
|
16017
16603
|
var OHW_LOADER_STYLE = {
|
|
16018
16604
|
position: "fixed",
|
|
@@ -16542,13 +17128,6 @@ function OhhwellsBridge() {
|
|
|
16542
17128
|
const [isItemDragging, setIsItemDragging] = useState13(false);
|
|
16543
17129
|
const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
|
|
16544
17130
|
isFooterFrameSelectionRef.current = isFooterFrameSelection;
|
|
16545
|
-
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
16546
|
-
const floatingPanelOpenRef = useRef10(false);
|
|
16547
|
-
floatingPanelOpenRef.current = floatingPanel !== null;
|
|
16548
|
-
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
16549
|
-
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
16550
|
-
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
16551
|
-
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
16552
17131
|
const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
|
|
16553
17132
|
const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
|
|
16554
17133
|
const footerDragRef = useRef10(null);
|
|
@@ -16566,6 +17145,13 @@ function OhhwellsBridge() {
|
|
|
16566
17145
|
const brandKitRef = useRef10("");
|
|
16567
17146
|
const stylesRef = useRef10("");
|
|
16568
17147
|
const pendingDeleteUndoRef = useRef10(null);
|
|
17148
|
+
const [floatingPanel, setFloatingPanel] = useState13(null);
|
|
17149
|
+
const floatingPanelOpenRef = useRef10(false);
|
|
17150
|
+
const setFloatingPanelRef = useRef10(setFloatingPanel);
|
|
17151
|
+
const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
|
|
17152
|
+
const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
|
|
17153
|
+
const [editorViewport, setEditorViewport] = useState13("desktop");
|
|
17154
|
+
const [parentScrollSnap, setParentScrollSnap] = useState13(null);
|
|
16569
17155
|
const [sitePages, setSitePages] = useState13([]);
|
|
16570
17156
|
const [sectionsByPath, setSectionsByPath] = useState13({});
|
|
16571
17157
|
const sectionsPrefetchGenRef = useRef10(0);
|
|
@@ -16574,7 +17160,18 @@ function OhhwellsBridge() {
|
|
|
16574
17160
|
const linkPopoverOpenRef = useRef10(false);
|
|
16575
17161
|
const linkPopoverGraceUntilRef = useRef10(0);
|
|
16576
17162
|
setLinkPopoverRef.current = setLinkPopover;
|
|
17163
|
+
setFloatingPanelRef.current = setFloatingPanel;
|
|
16577
17164
|
linkPopoverSessionRef.current = linkPopover;
|
|
17165
|
+
floatingPanelOpenRef.current = Boolean(floatingPanel);
|
|
17166
|
+
useEffect13(() => {
|
|
17167
|
+
const syncViewport = () => {
|
|
17168
|
+
const next = window.innerWidth <= 480 ? "mobile" : "desktop";
|
|
17169
|
+
setEditorViewport((prev) => prev === next ? prev : next);
|
|
17170
|
+
};
|
|
17171
|
+
syncViewport();
|
|
17172
|
+
window.addEventListener("resize", syncViewport);
|
|
17173
|
+
return () => window.removeEventListener("resize", syncViewport);
|
|
17174
|
+
}, []);
|
|
16578
17175
|
const {
|
|
16579
17176
|
navDragRef,
|
|
16580
17177
|
navDropSlots,
|
|
@@ -17899,17 +18496,19 @@ function OhhwellsBridge() {
|
|
|
17899
18496
|
}
|
|
17900
18497
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
17901
18498
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
18499
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
17902
18500
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
17903
18501
|
}
|
|
17904
18502
|
applyBrandChrome(content);
|
|
18503
|
+
initSectionInstancesFromContent(content, window.location.pathname);
|
|
17905
18504
|
for (const [key, val] of Object.entries(content)) {
|
|
17906
18505
|
if (key === "__ohw_sections") continue;
|
|
17907
18506
|
if (key === AI_SECTIONS_KEY) continue;
|
|
18507
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18508
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17908
18509
|
if (key === BRAND_KIT_KEY) continue;
|
|
17909
18510
|
if (key === STYLE_STORE_KEY) continue;
|
|
17910
18511
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
17911
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
17912
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
17913
18512
|
if (applyVideoSettingNode(key, val)) continue;
|
|
17914
18513
|
if (applyCarouselNode(key, val)) continue;
|
|
17915
18514
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -17937,6 +18536,8 @@ function OhhwellsBridge() {
|
|
|
17937
18536
|
}
|
|
17938
18537
|
} else if (el.dataset.ohwEditable === "link") {
|
|
17939
18538
|
applyLinkHref(el, val);
|
|
18539
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
18540
|
+
applyMapQuery(el, val);
|
|
17940
18541
|
} else if (el.dataset.ohwEditable === "icon") {
|
|
17941
18542
|
applyIconMarkup(el, val);
|
|
17942
18543
|
} else if (el.dataset.ohwEditable === "form") {
|
|
@@ -17957,7 +18558,6 @@ function OhhwellsBridge() {
|
|
|
17957
18558
|
if (isEditModeRef.current) requestMissingSocialIconsRef.current();
|
|
17958
18559
|
enforceLinkHrefs();
|
|
17959
18560
|
initSectionsFromContent(content, true);
|
|
17960
|
-
initSectionInstancesFromContent(content, window.location.pathname);
|
|
17961
18561
|
sectionsLoadedRef.current = true;
|
|
17962
18562
|
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
17963
18563
|
if (imageLoads.length === 0) return Promise.resolve();
|
|
@@ -17976,7 +18576,9 @@ function OhhwellsBridge() {
|
|
|
17976
18576
|
let cancelled = false;
|
|
17977
18577
|
setFetchState("loading");
|
|
17978
18578
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
17979
|
-
|
|
18579
|
+
const initialPath = pathname;
|
|
18580
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
18581
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
17980
18582
|
if (cancelled) return;
|
|
17981
18583
|
const content = data?.content ?? {};
|
|
17982
18584
|
const branding = Boolean(data?.showBranding);
|
|
@@ -18110,16 +18712,17 @@ function OhhwellsBridge() {
|
|
|
18110
18712
|
applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
|
|
18111
18713
|
}
|
|
18112
18714
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
18715
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
18113
18716
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
18114
18717
|
}
|
|
18115
18718
|
for (const [key, val] of Object.entries(content)) {
|
|
18116
18719
|
if (key === "__ohw_sections") continue;
|
|
18117
18720
|
if (key === AI_SECTIONS_KEY) continue;
|
|
18721
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18722
|
+
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18118
18723
|
if (key === BRAND_KIT_KEY) continue;
|
|
18119
18724
|
if (key === STYLE_STORE_KEY) continue;
|
|
18120
18725
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
18121
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
18122
|
-
if (LOGO_IMAGE_KEYS.includes(key)) continue;
|
|
18123
18726
|
if (applyVideoSettingNode(key, val)) continue;
|
|
18124
18727
|
if (applyCarouselNode(key, val)) continue;
|
|
18125
18728
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -18134,6 +18737,8 @@ function OhhwellsBridge() {
|
|
|
18134
18737
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
18135
18738
|
} else if (el.dataset.ohwEditable === "link") {
|
|
18136
18739
|
applyLinkHref(el, val);
|
|
18740
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
18741
|
+
applyMapQuery(el, val);
|
|
18137
18742
|
} else if (el.dataset.ohwEditable === "form") {
|
|
18138
18743
|
} else if (isIconMarkupValue(val)) {
|
|
18139
18744
|
} else if (el.innerHTML !== val) {
|
|
@@ -18165,6 +18770,17 @@ function OhhwellsBridge() {
|
|
|
18165
18770
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
18166
18771
|
};
|
|
18167
18772
|
applyFromCache();
|
|
18773
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
18774
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
18775
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
18776
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
18777
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
18778
|
+
if (!data?.content) return;
|
|
18779
|
+
contentCache.set(subdomain, data.content);
|
|
18780
|
+
applyFromCache();
|
|
18781
|
+
}).catch(() => {
|
|
18782
|
+
});
|
|
18783
|
+
}
|
|
18168
18784
|
observer = new MutationObserver(scheduleApply);
|
|
18169
18785
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
18170
18786
|
return () => {
|
|
@@ -18280,26 +18896,11 @@ function OhhwellsBridge() {
|
|
|
18280
18896
|
const t2 = setTimeout(measure, 500);
|
|
18281
18897
|
const ro = new ResizeObserver(schedule);
|
|
18282
18898
|
ro.observe(document.body);
|
|
18283
|
-
let lastWidth = window.innerWidth;
|
|
18284
|
-
let resizeTimers = [];
|
|
18285
|
-
const clearResizeTimers = () => {
|
|
18286
|
-
resizeTimers.forEach(clearTimeout);
|
|
18287
|
-
resizeTimers = [];
|
|
18288
|
-
};
|
|
18289
|
-
const handleResize = () => {
|
|
18290
|
-
if (window.innerWidth === lastWidth) return;
|
|
18291
|
-
lastWidth = window.innerWidth;
|
|
18292
|
-
clearResizeTimers();
|
|
18293
|
-
resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
|
|
18294
|
-
};
|
|
18295
|
-
window.addEventListener("resize", handleResize);
|
|
18296
18899
|
return () => {
|
|
18297
18900
|
clearTimeout(t1);
|
|
18298
18901
|
clearTimeout(t2);
|
|
18299
18902
|
if (raf != null) cancelAnimationFrame(raf);
|
|
18300
18903
|
ro.disconnect();
|
|
18301
|
-
clearResizeTimers();
|
|
18302
|
-
window.removeEventListener("resize", handleResize);
|
|
18303
18904
|
};
|
|
18304
18905
|
}, [pathname, isEditMode, postToParent2]);
|
|
18305
18906
|
useEffect13(() => {
|
|
@@ -18343,15 +18944,6 @@ function OhhwellsBridge() {
|
|
|
18343
18944
|
[style*="100vh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
|
|
18344
18945
|
[style*="100svh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
|
|
18345
18946
|
[style*="100dvh"] { min-height: ${initialVh}px !important; height: ${initialVh}px !important; }
|
|
18346
|
-
/* A section written as min-height: var(--ohw-canvas-h, 100svh) \u2014 the documented way to
|
|
18347
|
-
build a full-viewport section that still grows for its own content \u2014 matches the three
|
|
18348
|
-
rules above on substring alone, since the fallback text contains "100svh" too. Forcing
|
|
18349
|
-
a literal height on top of that turns "at least one screen" into "exactly one screen",
|
|
18350
|
-
so content taller than one screen (a long mobile hero, say) overflows a centered flex
|
|
18351
|
-
column upward, under whatever sits above it. Only the min-height half belongs to it. */
|
|
18352
|
-
[style*="min-height"][style*="100vh"],
|
|
18353
|
-
[style*="min-height"][style*="100svh"],
|
|
18354
|
-
[style*="min-height"][style*="100dvh"] { height: auto !important; }
|
|
18355
18947
|
/* Emptied text keeps somewhere to click. A label typed down to nothing collapses to a
|
|
18356
18948
|
couple of pixels, and getting back into it meant hunting for the caret with the mouse.
|
|
18357
18949
|
Edit mode only \u2014 the published page shows nothing where there is nothing (OHH-736). */
|
|
@@ -18554,9 +19146,6 @@ function OhhwellsBridge() {
|
|
|
18554
19146
|
if (target.closest("[data-ohw-state-toggle]")) return;
|
|
18555
19147
|
if (target.closest("[data-ohw-max-badge]")) return;
|
|
18556
19148
|
if (isInsideLinkEditor(target)) return;
|
|
18557
|
-
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
18558
|
-
clearMediaSelectionRef.current();
|
|
18559
|
-
}
|
|
18560
19149
|
if (isInsideFloatingPanel(target)) return;
|
|
18561
19150
|
if (target.closest("[data-ohw-form-toolbar]")) return;
|
|
18562
19151
|
if (target.closest(
|
|
@@ -18564,6 +19153,9 @@ function OhhwellsBridge() {
|
|
|
18564
19153
|
)) {
|
|
18565
19154
|
return;
|
|
18566
19155
|
}
|
|
19156
|
+
if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
|
|
19157
|
+
clearMediaSelectionRef.current();
|
|
19158
|
+
}
|
|
18567
19159
|
{
|
|
18568
19160
|
const formEl = getFormElement(target);
|
|
18569
19161
|
const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
|
|
@@ -18715,14 +19307,6 @@ function OhhwellsBridge() {
|
|
|
18715
19307
|
}
|
|
18716
19308
|
const clickedButton = findClosestButtonLike(target);
|
|
18717
19309
|
const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
|
|
18718
|
-
console.log("[click-debug]", {
|
|
18719
|
-
editableType: editable.dataset.ohwEditable,
|
|
18720
|
-
editableTag: editable.tagName,
|
|
18721
|
-
targetTag: target.tagName,
|
|
18722
|
-
clickedButtonTag: clickedButton?.tagName ?? null,
|
|
18723
|
-
buttonOnMedia,
|
|
18724
|
-
isMediaEditableEditable: isMediaEditable(editable)
|
|
18725
|
-
});
|
|
18726
19310
|
if (isMediaEditable(editable) && !buttonOnMedia) {
|
|
18727
19311
|
e.preventDefault();
|
|
18728
19312
|
e.stopPropagation();
|
|
@@ -18749,11 +19333,6 @@ function OhhwellsBridge() {
|
|
|
18749
19333
|
const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
|
|
18750
19334
|
const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
|
|
18751
19335
|
const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
|
|
18752
|
-
console.log("[click-debug 2]", {
|
|
18753
|
-
hrefLookupTargetTag: hrefLookupTarget.tagName,
|
|
18754
|
-
hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
|
|
18755
|
-
navAnchorTag: navAnchor?.tagName ?? null
|
|
18756
|
-
});
|
|
18757
19336
|
if (navAnchor) {
|
|
18758
19337
|
e.preventDefault();
|
|
18759
19338
|
e.stopPropagation();
|
|
@@ -18923,6 +19502,9 @@ function OhhwellsBridge() {
|
|
|
18923
19502
|
setHoveredItemRect(null);
|
|
18924
19503
|
hoveredNavContainerRef.current = null;
|
|
18925
19504
|
setHoveredNavContainerRect(null);
|
|
19505
|
+
siblingHintElRef.current = null;
|
|
19506
|
+
setSiblingHintRect(null);
|
|
19507
|
+
setSiblingHintRects([]);
|
|
18926
19508
|
return;
|
|
18927
19509
|
}
|
|
18928
19510
|
{
|
|
@@ -19041,7 +19623,6 @@ function OhhwellsBridge() {
|
|
|
19041
19623
|
hoveredNavContainerRef.current = null;
|
|
19042
19624
|
setHoveredNavContainerRect(null);
|
|
19043
19625
|
hoveredItemElRef.current = editable;
|
|
19044
|
-
setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
|
|
19045
19626
|
}
|
|
19046
19627
|
}
|
|
19047
19628
|
}
|
|
@@ -19338,7 +19919,7 @@ function OhhwellsBridge() {
|
|
|
19338
19919
|
}
|
|
19339
19920
|
};
|
|
19340
19921
|
const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
|
|
19341
|
-
if (linkPopoverOpenRef.current) {
|
|
19922
|
+
if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
19342
19923
|
if (hoveredImageRef.current) {
|
|
19343
19924
|
hoveredImageRef.current = null;
|
|
19344
19925
|
hoveredImageHasTextOverlapRef.current = false;
|
|
@@ -19703,8 +20284,7 @@ function OhhwellsBridge() {
|
|
|
19703
20284
|
};
|
|
19704
20285
|
const handleMouseMove = (e) => {
|
|
19705
20286
|
const { clientX, clientY } = e;
|
|
19706
|
-
if (
|
|
19707
|
-
if (isOverEditorChrome(clientX, clientY)) {
|
|
20287
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
|
|
19708
20288
|
document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
|
|
19709
20289
|
formHoverElRef.current = null;
|
|
19710
20290
|
setFormHoverRect(null);
|
|
@@ -19712,6 +20292,12 @@ function OhhwellsBridge() {
|
|
|
19712
20292
|
setHoveredItemRect(null);
|
|
19713
20293
|
hoveredNavContainerRef.current = null;
|
|
19714
20294
|
setHoveredNavContainerRect(null);
|
|
20295
|
+
siblingHintElRef.current = null;
|
|
20296
|
+
setSiblingHintRect(null);
|
|
20297
|
+
setSiblingHintRects([]);
|
|
20298
|
+
dismissImageHover();
|
|
20299
|
+
clearImageHover();
|
|
20300
|
+
setSectionGap(null);
|
|
19715
20301
|
return;
|
|
19716
20302
|
}
|
|
19717
20303
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
@@ -19723,7 +20309,11 @@ function OhhwellsBridge() {
|
|
|
19723
20309
|
if (e.data?.type !== "ow:pointer-sync") return;
|
|
19724
20310
|
const { clientX, clientY } = e.data;
|
|
19725
20311
|
if (typeof clientX !== "number" || typeof clientY !== "number") return;
|
|
19726
|
-
if (
|
|
20312
|
+
if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
|
|
20313
|
+
dismissImageHover();
|
|
20314
|
+
clearImageHover();
|
|
20315
|
+
return;
|
|
20316
|
+
}
|
|
19727
20317
|
if (probeSocialsRowAt(clientX, clientY)) return;
|
|
19728
20318
|
probeSectionGapAt(clientX, clientY);
|
|
19729
20319
|
probeImageAt(clientX, clientY);
|
|
@@ -20002,6 +20592,44 @@ function OhhwellsBridge() {
|
|
|
20002
20592
|
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20003
20593
|
}, 400));
|
|
20004
20594
|
};
|
|
20595
|
+
const reapCommittedAiSections = (excludeIds) => {
|
|
20596
|
+
const aiState = parseAiSectionsState(aiSectionsRef.current);
|
|
20597
|
+
if (aiState.sections.length === 0) return [];
|
|
20598
|
+
let orderEntries = [];
|
|
20599
|
+
try {
|
|
20600
|
+
const parsed = JSON.parse(editContentRef.current[SECTION_ORDER_KEY] ?? "[]");
|
|
20601
|
+
if (Array.isArray(parsed)) orderEntries = parsed;
|
|
20602
|
+
} catch {
|
|
20603
|
+
return [];
|
|
20604
|
+
}
|
|
20605
|
+
const removedIds = orderEntries.filter((entry) => entry && typeof entry.instanceId === "string" && entry.removed).map((entry) => entry.instanceId);
|
|
20606
|
+
if (removedIds.length === 0) return [];
|
|
20607
|
+
const result = reapRemovedAiSections(aiState, parseStyleStore(stylesRef.current), removedIds, excludeIds);
|
|
20608
|
+
if (!result.changed) return [];
|
|
20609
|
+
const nodes = [];
|
|
20610
|
+
aiSectionsRef.current = serializeAiSectionsState(result.state);
|
|
20611
|
+
nodes.push({ key: AI_SECTIONS_KEY, text: aiSectionsRef.current });
|
|
20612
|
+
const reaped = new Set(result.reapedIds);
|
|
20613
|
+
const nextOrderJson = JSON.stringify(orderEntries.filter((entry) => !reaped.has(entry.instanceId)));
|
|
20614
|
+
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: nextOrderJson };
|
|
20615
|
+
setAiSectionOrder(nextOrderJson, window.location.pathname);
|
|
20616
|
+
nodes.push({ key: SECTION_ORDER_KEY, text: nextOrderJson });
|
|
20617
|
+
if (result.store) {
|
|
20618
|
+
stylesRef.current = JSON.stringify(result.store);
|
|
20619
|
+
nodes.push({ key: STYLE_STORE_KEY, text: stylesRef.current });
|
|
20620
|
+
}
|
|
20621
|
+
const nextContent = { ...editContentRef.current };
|
|
20622
|
+
for (const key of Object.keys(nextContent)) {
|
|
20623
|
+
if (key.startsWith(AI_SLOT_KEY_PREFIX) && result.slotPrefixes.some((prefix) => key.startsWith(prefix)) && nextContent[key] !== "") {
|
|
20624
|
+
nextContent[key] = "";
|
|
20625
|
+
nodes.push({ key, text: "" });
|
|
20626
|
+
}
|
|
20627
|
+
}
|
|
20628
|
+
editContentRef.current = nextContent;
|
|
20629
|
+
applyAiSectionsToDom(result.state);
|
|
20630
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20631
|
+
return nodes;
|
|
20632
|
+
};
|
|
20005
20633
|
const handleHydrate = (e) => {
|
|
20006
20634
|
if (e.data?.type !== "ow:hydrate") return;
|
|
20007
20635
|
const content = e.data.content;
|
|
@@ -20020,9 +20648,11 @@ function OhhwellsBridge() {
|
|
|
20020
20648
|
}
|
|
20021
20649
|
if (typeof content[STYLE_STORE_KEY] === "string") {
|
|
20022
20650
|
stylesRef.current = content[STYLE_STORE_KEY];
|
|
20651
|
+
document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
|
|
20023
20652
|
applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
|
|
20024
20653
|
}
|
|
20025
20654
|
applyBrandChrome(content);
|
|
20655
|
+
initSectionInstancesFromContent(content, window.location.pathname);
|
|
20026
20656
|
let sectionsJson = null;
|
|
20027
20657
|
for (const [key, val] of Object.entries(content)) {
|
|
20028
20658
|
if (key === "__ohw_sections") {
|
|
@@ -20030,11 +20660,11 @@ function OhhwellsBridge() {
|
|
|
20030
20660
|
continue;
|
|
20031
20661
|
}
|
|
20032
20662
|
if (key === AI_SECTIONS_KEY) continue;
|
|
20663
|
+
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
20664
|
+
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
20033
20665
|
if (key === BRAND_KIT_KEY) continue;
|
|
20034
20666
|
if (key === STYLE_STORE_KEY) continue;
|
|
20035
20667
|
if (BRAND_CHROME_KEYS.has(key)) continue;
|
|
20036
|
-
if (key === LOGO_PLACEHOLDER_KEY) continue;
|
|
20037
|
-
if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
|
|
20038
20668
|
if (applyVideoSettingNode(key, val)) continue;
|
|
20039
20669
|
if (applyCarouselNode(key, val)) continue;
|
|
20040
20670
|
document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
|
|
@@ -20048,6 +20678,8 @@ function OhhwellsBridge() {
|
|
|
20048
20678
|
if (video && video.src !== val) applyVideoSrc(video, val);
|
|
20049
20679
|
} else if (el.dataset.ohwEditable === "link") {
|
|
20050
20680
|
applyLinkHref(el, val);
|
|
20681
|
+
} else if (el.dataset.ohwEditable === "map") {
|
|
20682
|
+
applyMapQuery(el, val);
|
|
20051
20683
|
} else if (el.dataset.ohwEditable === "icon") {
|
|
20052
20684
|
applyIconMarkup(el, val);
|
|
20053
20685
|
} else if (isIconMarkupValue(val)) {
|
|
@@ -20064,12 +20696,16 @@ function OhhwellsBridge() {
|
|
|
20064
20696
|
sectionsLoadedRef.current = true;
|
|
20065
20697
|
pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
|
|
20066
20698
|
}
|
|
20067
|
-
initSectionInstancesFromContent(content, window.location.pathname);
|
|
20068
20699
|
editContentRef.current = { ...editContentRef.current, ...content };
|
|
20069
20700
|
reconcileNavbarItemsFromContent(editContentRef.current);
|
|
20070
20701
|
reconcileFooterOrderFromContent(editContentRef.current);
|
|
20071
20702
|
syncNavigationDragCursorAttrs();
|
|
20072
20703
|
enforceLinkHrefs();
|
|
20704
|
+
const hydrateReapExclude = /* @__PURE__ */ new Set();
|
|
20705
|
+
const hydratePendingUndo = pendingDeleteUndoRef.current;
|
|
20706
|
+
if (hydratePendingUndo?.sectionInstanceId) hydrateReapExclude.add(hydratePendingUndo.sectionInstanceId);
|
|
20707
|
+
const reapNodes = reapCommittedAiSections(hydrateReapExclude);
|
|
20708
|
+
if (reapNodes.length > 0) postToParentRef.current({ type: "ow:change", nodes: reapNodes });
|
|
20073
20709
|
const hydratedHeight = document.body.scrollHeight;
|
|
20074
20710
|
if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
|
|
20075
20711
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
@@ -20209,12 +20845,35 @@ function OhhwellsBridge() {
|
|
|
20209
20845
|
window.addEventListener("message", handleAiSetBrand);
|
|
20210
20846
|
const handleAiSetStyles = (e) => {
|
|
20211
20847
|
if (e.data?.type !== "ow:ai-set-styles") return;
|
|
20212
|
-
|
|
20848
|
+
let value = typeof e.data.value === "string" ? e.data.value : "";
|
|
20213
20849
|
const previous = stylesRef.current;
|
|
20850
|
+
let previousSections;
|
|
20851
|
+
const store = parseStyleStore(value);
|
|
20852
|
+
if (store) {
|
|
20853
|
+
const folded = foldAlignIntoTrees(parseAiSectionsState(aiSectionsRef.current), store);
|
|
20854
|
+
if (folded.changed) {
|
|
20855
|
+
const nextSections = serializeAiSectionsState(folded.state);
|
|
20856
|
+
if (nextSections !== aiSectionsRef.current) {
|
|
20857
|
+
previousSections = aiSectionsRef.current;
|
|
20858
|
+
aiSectionsRef.current = nextSections;
|
|
20859
|
+
applyAiSectionsToDom(folded.state);
|
|
20860
|
+
postToParentRef.current({
|
|
20861
|
+
type: "ow:change",
|
|
20862
|
+
nodes: [{ key: AI_SECTIONS_KEY, text: nextSections }]
|
|
20863
|
+
});
|
|
20864
|
+
}
|
|
20865
|
+
value = JSON.stringify(folded.store);
|
|
20866
|
+
}
|
|
20867
|
+
}
|
|
20214
20868
|
stylesRef.current = value;
|
|
20215
20869
|
applyStylesToDom(parseStyleStore(value));
|
|
20216
20870
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
|
|
20217
|
-
postToParentRef.current({
|
|
20871
|
+
postToParentRef.current({
|
|
20872
|
+
type: "ow:ai-styles-applied",
|
|
20873
|
+
previous,
|
|
20874
|
+
value,
|
|
20875
|
+
...previousSections !== void 0 ? { previousSections } : {}
|
|
20876
|
+
});
|
|
20218
20877
|
};
|
|
20219
20878
|
window.addEventListener("message", handleAiSetStyles);
|
|
20220
20879
|
const handleGetBrand = (e) => {
|
|
@@ -20231,8 +20890,11 @@ function OhhwellsBridge() {
|
|
|
20231
20890
|
if (!instanceId || !direction) return;
|
|
20232
20891
|
const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
|
|
20233
20892
|
if (!entries) return;
|
|
20234
|
-
const orderJson = JSON.stringify(
|
|
20893
|
+
const orderJson = JSON.stringify(
|
|
20894
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
20895
|
+
);
|
|
20235
20896
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20897
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
20236
20898
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20237
20899
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
20238
20900
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20251,8 +20913,11 @@ function OhhwellsBridge() {
|
|
|
20251
20913
|
const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
|
|
20252
20914
|
const entries = deleteSectionInstance(instanceId, window.location.pathname, currentEntries);
|
|
20253
20915
|
if (!entries) return;
|
|
20254
|
-
const orderJson = JSON.stringify(
|
|
20916
|
+
const orderJson = JSON.stringify(
|
|
20917
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
20918
|
+
);
|
|
20255
20919
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20920
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20256
20921
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
20257
20922
|
aiSectionApiRef.current?.clear();
|
|
20258
20923
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20261,6 +20926,7 @@ function OhhwellsBridge() {
|
|
|
20261
20926
|
const actionId = newInstanceId();
|
|
20262
20927
|
pendingDeleteUndoRef.current = {
|
|
20263
20928
|
actionId,
|
|
20929
|
+
sectionInstanceId: instanceId,
|
|
20264
20930
|
restore: () => {
|
|
20265
20931
|
const restoredEntries = getPageSectionOrderEntries(
|
|
20266
20932
|
editContentRef.current[SECTION_ORDER_KEY],
|
|
@@ -20268,8 +20934,11 @@ function OhhwellsBridge() {
|
|
|
20268
20934
|
);
|
|
20269
20935
|
const restored = restoreSectionInstance(instanceId, window.location.pathname, restoredEntries);
|
|
20270
20936
|
if (!restored) return;
|
|
20271
|
-
const restoredJson = JSON.stringify(
|
|
20937
|
+
const restoredJson = JSON.stringify(
|
|
20938
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, restored)
|
|
20939
|
+
);
|
|
20272
20940
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
|
|
20941
|
+
setAiSectionOrder(restoredJson, window.location.pathname);
|
|
20273
20942
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
|
|
20274
20943
|
window.dispatchEvent(new Event("resize"));
|
|
20275
20944
|
const restoreHeight = document.body.scrollHeight;
|
|
@@ -20286,6 +20955,37 @@ function OhhwellsBridge() {
|
|
|
20286
20955
|
});
|
|
20287
20956
|
};
|
|
20288
20957
|
window.addEventListener("message", handleDeleteSection);
|
|
20958
|
+
const handleDuplicateSection = (e) => {
|
|
20959
|
+
if (e.data?.type !== "ow:duplicate-section") return;
|
|
20960
|
+
const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
|
|
20961
|
+
if (!instanceId) return;
|
|
20962
|
+
const newId = newInstanceId();
|
|
20963
|
+
const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
|
|
20964
|
+
const result = duplicateSectionInstance(instanceId, newId, window.location.pathname, currentEntries);
|
|
20965
|
+
if (!result) return;
|
|
20966
|
+
const { entries, keyRekeys } = result;
|
|
20967
|
+
const orderJson = JSON.stringify(
|
|
20968
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
20969
|
+
);
|
|
20970
|
+
const nodes = [{ key: SECTION_ORDER_KEY, text: orderJson }];
|
|
20971
|
+
for (const { from, to } of keyRekeys) {
|
|
20972
|
+
const inherited = editContentRef.current[from];
|
|
20973
|
+
if (inherited !== void 0) nodes.push({ key: to, text: inherited });
|
|
20974
|
+
}
|
|
20975
|
+
editContentRef.current = {
|
|
20976
|
+
...editContentRef.current,
|
|
20977
|
+
...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
|
|
20978
|
+
};
|
|
20979
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
20980
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20981
|
+
postToParentRef.current({ type: "ow:change", nodes });
|
|
20982
|
+
window.dispatchEvent(new Event("resize"));
|
|
20983
|
+
const duplicateHeight = document.body.scrollHeight;
|
|
20984
|
+
if (duplicateHeight > 50) postToParentRef.current({ type: "ow:height", height: duplicateHeight });
|
|
20985
|
+
const clone = document.querySelector(`[data-ohw-instance="${CSS.escape(newId)}"]`);
|
|
20986
|
+
if (clone) aiSectionApiRef.current?.selectFromElement(clone);
|
|
20987
|
+
};
|
|
20988
|
+
window.addEventListener("message", handleDuplicateSection);
|
|
20289
20989
|
const handleDeactivate = (e) => {
|
|
20290
20990
|
if (e.data?.type !== "ow:deactivate") return;
|
|
20291
20991
|
if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return;
|
|
@@ -20295,6 +20995,12 @@ function OhhwellsBridge() {
|
|
|
20295
20995
|
closeLinkPopoverRef.current();
|
|
20296
20996
|
return;
|
|
20297
20997
|
}
|
|
20998
|
+
if (floatingPanelOpenRef.current) {
|
|
20999
|
+
setFloatingPanelRef.current(null);
|
|
21000
|
+
deselectRef.current();
|
|
21001
|
+
deactivateRef.current();
|
|
21002
|
+
return;
|
|
21003
|
+
}
|
|
20298
21004
|
deselectRef.current();
|
|
20299
21005
|
deactivateRef.current();
|
|
20300
21006
|
clearMediaSelectionRef.current();
|
|
@@ -20540,6 +21246,10 @@ function OhhwellsBridge() {
|
|
|
20540
21246
|
};
|
|
20541
21247
|
const handleSave = (e) => {
|
|
20542
21248
|
if (e.data?.type !== "ow:save") return;
|
|
21249
|
+
const pendingUndo = pendingDeleteUndoRef.current;
|
|
21250
|
+
const reapExclude = /* @__PURE__ */ new Set();
|
|
21251
|
+
if (pendingUndo?.sectionInstanceId) reapExclude.add(pendingUndo.sectionInstanceId);
|
|
21252
|
+
const reapNodes = reapCommittedAiSections(reapExclude);
|
|
20543
21253
|
const nodes = collectEditableNodes(editContentRef.current);
|
|
20544
21254
|
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
20545
21255
|
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
@@ -20559,6 +21269,11 @@ function OhhwellsBridge() {
|
|
|
20559
21269
|
const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
|
|
20560
21270
|
if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
|
|
20561
21271
|
});
|
|
21272
|
+
for (const reapNode of reapNodes) {
|
|
21273
|
+
if (reapNode.key.startsWith(AI_SLOT_KEY_PREFIX) && !nodes.some((n) => n.key === reapNode.key)) {
|
|
21274
|
+
nodes.push({ key: reapNode.key, type: "text", text: reapNode.text });
|
|
21275
|
+
}
|
|
21276
|
+
}
|
|
20562
21277
|
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
20563
21278
|
};
|
|
20564
21279
|
const handleInsertSection = (e) => {
|
|
@@ -20569,8 +21284,12 @@ function OhhwellsBridge() {
|
|
|
20569
21284
|
if (inserted) {
|
|
20570
21285
|
const tracker = getSectionsTracker();
|
|
20571
21286
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
20572
|
-
const
|
|
20573
|
-
|
|
21287
|
+
const reportHeight = () => {
|
|
21288
|
+
const h = document.body.scrollHeight;
|
|
21289
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
21290
|
+
};
|
|
21291
|
+
reportHeight();
|
|
21292
|
+
setTimeout(reportHeight, 500);
|
|
20574
21293
|
}
|
|
20575
21294
|
};
|
|
20576
21295
|
const handleSwitchSchedule = (e) => {
|
|
@@ -20972,11 +21691,12 @@ function OhhwellsBridge() {
|
|
|
20972
21691
|
window.removeEventListener("message", handleMoveSection);
|
|
20973
21692
|
window.removeEventListener("message", handlePanelDragging);
|
|
20974
21693
|
window.removeEventListener("message", handleDeleteSection);
|
|
21694
|
+
window.removeEventListener("message", handleDuplicateSection);
|
|
20975
21695
|
window.removeEventListener("message", handleDeactivate);
|
|
20976
|
-
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
20977
21696
|
window.removeEventListener("message", handleToastAction);
|
|
20978
21697
|
window.removeEventListener("message", handleFormCount);
|
|
20979
21698
|
window.removeEventListener("message", handleUiEscape);
|
|
21699
|
+
document.documentElement.removeAttribute("data-ohw-panel-dragging");
|
|
20980
21700
|
autoSaveTimers.current.forEach(clearTimeout);
|
|
20981
21701
|
autoSaveTimers.current.clear();
|
|
20982
21702
|
if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
|
|
@@ -21179,7 +21899,7 @@ function OhhwellsBridge() {
|
|
|
21179
21899
|
postToParent2({
|
|
21180
21900
|
type: "ow:ready",
|
|
21181
21901
|
version: "1",
|
|
21182
|
-
bridgeVersion: "0.1.
|
|
21902
|
+
bridgeVersion: "0.1.88",
|
|
21183
21903
|
path: pathname,
|
|
21184
21904
|
nodes: collectEditableNodes(editContentRef.current),
|
|
21185
21905
|
sections
|
|
@@ -22098,6 +22818,59 @@ function OhhwellsBridge() {
|
|
|
22098
22818
|
) : null
|
|
22099
22819
|
] });
|
|
22100
22820
|
}
|
|
22821
|
+
|
|
22822
|
+
// src/ui/EmptySection.tsx
|
|
22823
|
+
import Link3 from "next/link";
|
|
22824
|
+
import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
22825
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
22826
|
+
return /* @__PURE__ */ jsxs21(Fragment9, { children: [
|
|
22827
|
+
/* @__PURE__ */ jsx34(
|
|
22828
|
+
"p",
|
|
22829
|
+
{
|
|
22830
|
+
style: {
|
|
22831
|
+
fontFamily: "var(--brand-font-body)",
|
|
22832
|
+
fontSize: "0.75rem",
|
|
22833
|
+
fontWeight: 500,
|
|
22834
|
+
letterSpacing: "0.15em",
|
|
22835
|
+
textTransform: "uppercase",
|
|
22836
|
+
color: "var(--brand-accent)",
|
|
22837
|
+
marginBottom: "1.5rem"
|
|
22838
|
+
},
|
|
22839
|
+
children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
22840
|
+
}
|
|
22841
|
+
),
|
|
22842
|
+
/* @__PURE__ */ jsx34(
|
|
22843
|
+
"h1",
|
|
22844
|
+
{
|
|
22845
|
+
style: {
|
|
22846
|
+
fontFamily: "var(--brand-font-heading)",
|
|
22847
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
22848
|
+
lineHeight: 1.1,
|
|
22849
|
+
letterSpacing: "-0.025em",
|
|
22850
|
+
color: "var(--brand-text)",
|
|
22851
|
+
marginBottom: "1rem"
|
|
22852
|
+
},
|
|
22853
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
22854
|
+
children: title
|
|
22855
|
+
}
|
|
22856
|
+
),
|
|
22857
|
+
/* @__PURE__ */ jsx34(
|
|
22858
|
+
"p",
|
|
22859
|
+
{
|
|
22860
|
+
style: {
|
|
22861
|
+
fontFamily: "var(--brand-font-body)",
|
|
22862
|
+
fontSize: "1rem",
|
|
22863
|
+
lineHeight: 1.7,
|
|
22864
|
+
fontWeight: 300,
|
|
22865
|
+
color: "var(--brand-text-muted)",
|
|
22866
|
+
maxWidth: "340px"
|
|
22867
|
+
},
|
|
22868
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
22869
|
+
children: "This page doesn't have any content yet."
|
|
22870
|
+
}
|
|
22871
|
+
)
|
|
22872
|
+
] });
|
|
22873
|
+
}
|
|
22101
22874
|
export {
|
|
22102
22875
|
AI_DEFAULT_BRAND,
|
|
22103
22876
|
AI_TREE_SCHEMA_VERSIONS,
|
|
@@ -22114,6 +22887,7 @@ export {
|
|
|
22114
22887
|
DropdownMenuItem,
|
|
22115
22888
|
DropdownMenuSeparator,
|
|
22116
22889
|
DropdownMenuTrigger,
|
|
22890
|
+
EmptySection,
|
|
22117
22891
|
ItemActionToolbar,
|
|
22118
22892
|
ItemInteractionLayer,
|
|
22119
22893
|
LinkEditorPanel,
|