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