@ohhwells/bridge 0.1.94 → 0.1.96
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 +443 -274
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +443 -274
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -143,6 +143,9 @@ function isRenderableTree(value) {
|
|
|
143
143
|
// src/lib/ai-sections-store.ts
|
|
144
144
|
var AI_SECTIONS_KEY = "__ohw_ai_sections";
|
|
145
145
|
var AI_SLOT_KEY_PREFIX = "ai.";
|
|
146
|
+
function aiSlotKeyPrefixFor(sectionId) {
|
|
147
|
+
return `${AI_SLOT_KEY_PREFIX}${sectionId}.`;
|
|
148
|
+
}
|
|
146
149
|
var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
|
|
147
150
|
function parseAiSectionsState(raw) {
|
|
148
151
|
if (!raw) return EMPTY_AI_SECTIONS;
|
|
@@ -254,6 +257,33 @@ function deleteSectionFromState(state, sectionId) {
|
|
|
254
257
|
if (removed.includes(sectionId)) return state;
|
|
255
258
|
return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
|
|
256
259
|
}
|
|
260
|
+
function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE__ */ new Set()) {
|
|
261
|
+
const generated = new Set(state.sections.map((entry) => entry.id));
|
|
262
|
+
const reapedIds = [...new Set(removedIds)].filter((id) => generated.has(id) && !excludeIds.has(id));
|
|
263
|
+
if (reapedIds.length === 0) {
|
|
264
|
+
return { state, store, reapedIds: [], slotPrefixes: [], changed: false };
|
|
265
|
+
}
|
|
266
|
+
const reaped = new Set(reapedIds);
|
|
267
|
+
const slotPrefixes = reapedIds.map(aiSlotKeyPrefixFor);
|
|
268
|
+
const nextState = {
|
|
269
|
+
...state,
|
|
270
|
+
v: 1,
|
|
271
|
+
sections: state.sections.filter((entry) => !reaped.has(entry.id))
|
|
272
|
+
};
|
|
273
|
+
let nextStore = store;
|
|
274
|
+
if (store) {
|
|
275
|
+
const sections = {};
|
|
276
|
+
for (const [key, override] of Object.entries(store.sections)) {
|
|
277
|
+
if (!reaped.has(key)) sections[key] = override;
|
|
278
|
+
}
|
|
279
|
+
const nodes = {};
|
|
280
|
+
for (const [key, override] of Object.entries(store.nodes)) {
|
|
281
|
+
if (!slotPrefixes.some((prefix) => key.startsWith(prefix))) nodes[key] = override;
|
|
282
|
+
}
|
|
283
|
+
nextStore = { v: 1, sections, nodes };
|
|
284
|
+
}
|
|
285
|
+
return { state: nextState, store: nextStore, reapedIds, slotPrefixes, changed: true };
|
|
286
|
+
}
|
|
257
287
|
|
|
258
288
|
// src/lib/brand-chrome.ts
|
|
259
289
|
var BRAND_NAME_KEY = "__ohw_brand_name";
|
|
@@ -632,6 +662,254 @@ function applyStylesToDom(store) {
|
|
|
632
662
|
var import_react_dom = require("react-dom");
|
|
633
663
|
var import_client = require("react-dom/client");
|
|
634
664
|
|
|
665
|
+
// src/lib/sections.ts
|
|
666
|
+
var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
667
|
+
function isChromeSection(el) {
|
|
668
|
+
return el.matches("header, nav, footer, aside");
|
|
669
|
+
}
|
|
670
|
+
function titleCaseSectionId(id) {
|
|
671
|
+
return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
672
|
+
}
|
|
673
|
+
function parseSectionsFromRoot(root) {
|
|
674
|
+
const seen = /* @__PURE__ */ new Set();
|
|
675
|
+
const sections = [];
|
|
676
|
+
for (const el of root.querySelectorAll("[data-ohw-section]")) {
|
|
677
|
+
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
678
|
+
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
679
|
+
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
680
|
+
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
681
|
+
continue;
|
|
682
|
+
seen.add(id);
|
|
683
|
+
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
684
|
+
sections.push({ id, label });
|
|
685
|
+
}
|
|
686
|
+
return sections;
|
|
687
|
+
}
|
|
688
|
+
function collectSectionsFromDom() {
|
|
689
|
+
if (typeof document === "undefined") return [];
|
|
690
|
+
return parseSectionsFromRoot(document);
|
|
691
|
+
}
|
|
692
|
+
function parseSectionsFromHtml(html) {
|
|
693
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
694
|
+
return parseSectionsFromRoot(doc);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// src/lib/section-instances.ts
|
|
698
|
+
var SECTION_ORDER_KEY = "__ohw_section_order";
|
|
699
|
+
var REMOVED_ATTR = "data-ohw-section-removed";
|
|
700
|
+
function isRemovedSection(el) {
|
|
701
|
+
return el.hasAttribute(REMOVED_ATTR);
|
|
702
|
+
}
|
|
703
|
+
function movableUnit(el) {
|
|
704
|
+
return el.closest("[data-ohw-section-container]") ?? el;
|
|
705
|
+
}
|
|
706
|
+
function sectionTypeOf(el) {
|
|
707
|
+
return el.getAttribute("data-ohw-section") ?? el.querySelector("[data-ohw-section]")?.getAttribute("data-ohw-section") ?? "";
|
|
708
|
+
}
|
|
709
|
+
function sectionElementOf(el) {
|
|
710
|
+
return el.hasAttribute("data-ohw-section") ? el : el.querySelector("[data-ohw-section]") ?? el;
|
|
711
|
+
}
|
|
712
|
+
function collectTopLevelUnits(predicate) {
|
|
713
|
+
const seen = /* @__PURE__ */ new Set();
|
|
714
|
+
const result = [];
|
|
715
|
+
document.querySelectorAll("[data-ohw-section]").forEach((el) => {
|
|
716
|
+
if (!predicate(el)) return;
|
|
717
|
+
const unit = movableUnit(el);
|
|
718
|
+
if (unit.parentElement?.closest("[data-ohw-section],[data-ohw-section-container]")) return;
|
|
719
|
+
if (seen.has(unit)) return;
|
|
720
|
+
seen.add(unit);
|
|
721
|
+
result.push(unit);
|
|
722
|
+
});
|
|
723
|
+
return result;
|
|
724
|
+
}
|
|
725
|
+
function topLevelSections() {
|
|
726
|
+
return collectTopLevelUnits((el) => !isChromeSection(el) && !isRemovedSection(movableUnit(el)));
|
|
727
|
+
}
|
|
728
|
+
function instanceIdOf(el) {
|
|
729
|
+
return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
|
|
730
|
+
}
|
|
731
|
+
function findByInstanceId(instanceId) {
|
|
732
|
+
const escapedId = CSS.escape(instanceId);
|
|
733
|
+
const direct = document.querySelector(`[data-ohw-instance="${escapedId}"]`);
|
|
734
|
+
if (direct) return movableUnit(direct);
|
|
735
|
+
const bare = document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
|
|
736
|
+
return bare ? movableUnit(bare) : null;
|
|
737
|
+
}
|
|
738
|
+
function planSectionMove(instanceId, targetIndex, currentPath) {
|
|
739
|
+
const sections = topLevelSections();
|
|
740
|
+
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
741
|
+
if (index === -1) return null;
|
|
742
|
+
const dragged = sections[index];
|
|
743
|
+
const others = sections.filter((_, i) => i !== index);
|
|
744
|
+
const clamped = Math.max(0, Math.min(targetIndex, others.length));
|
|
745
|
+
const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
|
|
746
|
+
return reordered.map((el, order) => ({
|
|
747
|
+
instanceId: instanceIdOf(el),
|
|
748
|
+
type: sectionTypeOf(el),
|
|
749
|
+
order,
|
|
750
|
+
pagePath: currentPath
|
|
751
|
+
}));
|
|
752
|
+
}
|
|
753
|
+
function moveSectionInstance(instanceId, direction, currentPath) {
|
|
754
|
+
const sections = topLevelSections();
|
|
755
|
+
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
756
|
+
if (index === -1) return null;
|
|
757
|
+
const siblingIndex = direction === "up" ? index - 1 : index + 1;
|
|
758
|
+
if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
|
|
759
|
+
const entries = planSectionMove(instanceId, siblingIndex, currentPath);
|
|
760
|
+
if (!entries) return null;
|
|
761
|
+
applyPersistedOrder(entries);
|
|
762
|
+
return entries;
|
|
763
|
+
}
|
|
764
|
+
function syncRemovedFlags(entries) {
|
|
765
|
+
const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
|
|
766
|
+
document.querySelectorAll(`[${REMOVED_ATTR}]`).forEach((el) => {
|
|
767
|
+
if (!removedIds.has(instanceIdOf(el))) {
|
|
768
|
+
el.style.removeProperty("display");
|
|
769
|
+
el.removeAttribute(REMOVED_ATTR);
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
for (const id of removedIds) {
|
|
773
|
+
const el = findByInstanceId(id);
|
|
774
|
+
if (el) {
|
|
775
|
+
el.style.display = "none";
|
|
776
|
+
el.setAttribute(REMOVED_ATTR, "");
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
function applyPersistedOrder(entries) {
|
|
781
|
+
syncRemovedFlags(entries);
|
|
782
|
+
if (entries.length === 0) return;
|
|
783
|
+
const sections = topLevelSections();
|
|
784
|
+
if (sections.length === 0) return;
|
|
785
|
+
const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
|
|
786
|
+
const ordered = [...sections].sort((a, b) => {
|
|
787
|
+
const aOrder = orderIndex.get(instanceIdOf(a));
|
|
788
|
+
const bOrder = orderIndex.get(instanceIdOf(b));
|
|
789
|
+
if (aOrder === void 0 && bOrder === void 0) return 0;
|
|
790
|
+
if (aOrder === void 0) return 1;
|
|
791
|
+
if (bOrder === void 0) return -1;
|
|
792
|
+
return aOrder - bOrder;
|
|
793
|
+
});
|
|
794
|
+
let prev = null;
|
|
795
|
+
for (const el of ordered) {
|
|
796
|
+
if (prev) prev.after(el);
|
|
797
|
+
prev = el;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
|
|
801
|
+
if (!findByInstanceId(instanceId)) return null;
|
|
802
|
+
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
803
|
+
const allSections = collectTopLevelUnits((el) => !isChromeSection(el));
|
|
804
|
+
allSections.forEach((el, order) => {
|
|
805
|
+
const id = instanceIdOf(el);
|
|
806
|
+
if (!byId.has(id)) {
|
|
807
|
+
byId.set(id, { instanceId: id, type: sectionTypeOf(el), order, pagePath: currentPath });
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
const target = byId.get(instanceId);
|
|
811
|
+
if (!target) return null;
|
|
812
|
+
byId.set(instanceId, { ...target, removed });
|
|
813
|
+
const entries = Array.from(byId.values());
|
|
814
|
+
applyPersistedOrder(entries);
|
|
815
|
+
return entries;
|
|
816
|
+
}
|
|
817
|
+
function deleteSectionInstance(instanceId, currentPath, existingEntries) {
|
|
818
|
+
return setSectionRemoved(instanceId, currentPath, existingEntries, true);
|
|
819
|
+
}
|
|
820
|
+
function restoreSectionInstance(instanceId, currentPath, existingEntries) {
|
|
821
|
+
return setSectionRemoved(instanceId, currentPath, existingEntries, false);
|
|
822
|
+
}
|
|
823
|
+
function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
|
|
824
|
+
const original = findByInstanceId(instanceId);
|
|
825
|
+
if (!original) return null;
|
|
826
|
+
const clone = original.cloneNode(true);
|
|
827
|
+
clone.setAttribute("data-ohw-instance", newId);
|
|
828
|
+
const keyRekeys = rekeySectionSubtree(clone, newId);
|
|
829
|
+
original.insertAdjacentElement("afterend", clone);
|
|
830
|
+
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
831
|
+
const entries = topLevelSections().map((el, order) => {
|
|
832
|
+
const id = instanceIdOf(el);
|
|
833
|
+
return {
|
|
834
|
+
instanceId: id,
|
|
835
|
+
type: sectionTypeOf(el),
|
|
836
|
+
order,
|
|
837
|
+
pagePath: currentPath,
|
|
838
|
+
...byId.get(id)?.removed ? { removed: true } : {}
|
|
839
|
+
};
|
|
840
|
+
});
|
|
841
|
+
applyPersistedOrder(entries);
|
|
842
|
+
return { entries, keyRekeys };
|
|
843
|
+
}
|
|
844
|
+
function newInstanceId() {
|
|
845
|
+
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
846
|
+
}
|
|
847
|
+
function getPageSectionOrderEntries(raw, currentPath) {
|
|
848
|
+
if (!raw) return [];
|
|
849
|
+
try {
|
|
850
|
+
const entries = JSON.parse(raw);
|
|
851
|
+
return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
|
|
852
|
+
} catch {
|
|
853
|
+
return [];
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function mergePageSectionOrder(raw, currentPath, pageEntries) {
|
|
857
|
+
let all = [];
|
|
858
|
+
if (raw) {
|
|
859
|
+
try {
|
|
860
|
+
const parsed = JSON.parse(raw);
|
|
861
|
+
if (Array.isArray(parsed)) all = parsed;
|
|
862
|
+
} catch {
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const otherPages = all.filter((e) => e && e.pagePath && e.pagePath !== currentPath);
|
|
866
|
+
const pageIds = new Set(pageEntries.map((e) => e.instanceId));
|
|
867
|
+
const removedHere = all.filter(
|
|
868
|
+
(e) => e && (!e.pagePath || e.pagePath === currentPath) && e.removed && !pageIds.has(e.instanceId)
|
|
869
|
+
);
|
|
870
|
+
return [...otherPages, ...removedHere, ...pageEntries];
|
|
871
|
+
}
|
|
872
|
+
function rekeySectionSubtree(root, instanceId) {
|
|
873
|
+
const suffix = `::${instanceId}`;
|
|
874
|
+
const pairs = [];
|
|
875
|
+
const rekey = (el, attr) => {
|
|
876
|
+
const current = el.getAttribute(attr);
|
|
877
|
+
if (!current) return;
|
|
878
|
+
const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
|
|
879
|
+
const next = `${base}${suffix}`;
|
|
880
|
+
el.setAttribute(attr, next);
|
|
881
|
+
pairs.push({ from: current, to: next });
|
|
882
|
+
};
|
|
883
|
+
if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
|
|
884
|
+
if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
|
|
885
|
+
root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
|
|
886
|
+
root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
|
|
887
|
+
return pairs;
|
|
888
|
+
}
|
|
889
|
+
function initSectionInstancesFromContent(content, currentPath) {
|
|
890
|
+
document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
|
|
891
|
+
el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
|
|
892
|
+
});
|
|
893
|
+
document.querySelectorAll("[data-ohw-section-container]:not([data-ohw-instance])").forEach((el) => {
|
|
894
|
+
const type = sectionTypeOf(el);
|
|
895
|
+
if (type) el.setAttribute("data-ohw-instance", type);
|
|
896
|
+
});
|
|
897
|
+
const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
|
|
898
|
+
for (const entry of entries) {
|
|
899
|
+
if (entry.instanceId === entry.type) continue;
|
|
900
|
+
if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
|
|
901
|
+
const original = document.querySelector(
|
|
902
|
+
`[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
|
|
903
|
+
);
|
|
904
|
+
if (!original) continue;
|
|
905
|
+
const clone = original.cloneNode(true);
|
|
906
|
+
clone.setAttribute("data-ohw-instance", entry.instanceId);
|
|
907
|
+
rekeySectionSubtree(clone, entry.instanceId);
|
|
908
|
+
original.insertAdjacentElement("afterend", clone);
|
|
909
|
+
}
|
|
910
|
+
applyPersistedOrder(entries);
|
|
911
|
+
}
|
|
912
|
+
|
|
635
913
|
// src/ui/ai-tree/AiTreeRenderer.tsx
|
|
636
914
|
var import_react = __toESM(require("react"), 1);
|
|
637
915
|
var import_lucide_react = require("lucide-react");
|
|
@@ -674,6 +952,25 @@ var FEATURE_LINE_CSS = [
|
|
|
674
952
|
`background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
|
|
675
953
|
`mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
|
|
676
954
|
].join("");
|
|
955
|
+
function buttonShellStyle(ctx, fullWidth) {
|
|
956
|
+
const bs = ctx.buttonStyle;
|
|
957
|
+
if (bs) {
|
|
958
|
+
return {
|
|
959
|
+
borderRadius: bs.radius,
|
|
960
|
+
...bs.padding ? { padding: bs.padding } : {},
|
|
961
|
+
...bs.fontFamily ? { fontFamily: bs.fontFamily } : { fontFamily: ctx.brand.fonts.body },
|
|
962
|
+
...bs.fontSize ? { fontSize: bs.fontSize } : {},
|
|
963
|
+
...bs.fontWeight ? { fontWeight: bs.fontWeight } : {},
|
|
964
|
+
...bs.letterSpacing && bs.letterSpacing !== "normal" ? { letterSpacing: bs.letterSpacing } : {},
|
|
965
|
+
...bs.textTransform && bs.textTransform !== "none" ? { textTransform: bs.textTransform } : {}
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
return {
|
|
969
|
+
borderRadius: AI_TREE_TOKENS.radiusButton,
|
|
970
|
+
padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
|
|
971
|
+
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
972
|
+
};
|
|
973
|
+
}
|
|
677
974
|
function hexLuminance(color) {
|
|
678
975
|
const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
679
976
|
if (!m) return null;
|
|
@@ -690,6 +987,12 @@ function hexContrast(a, b) {
|
|
|
690
987
|
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
691
988
|
return (hi + 0.05) / (lo + 0.05);
|
|
692
989
|
}
|
|
990
|
+
function primaryButtonLabel(brand) {
|
|
991
|
+
const darkC = hexContrast(brand.palette.primary, brand.palette.dark);
|
|
992
|
+
const lightC = hexContrast(brand.palette.primary, AI_TREE_TOKENS.textPrimaryForeground);
|
|
993
|
+
if (darkC === null || lightC === null) return AI_TREE_TOKENS.textPrimaryForeground;
|
|
994
|
+
return darkC > lightC ? brand.palette.dark : AI_TREE_TOKENS.textPrimaryForeground;
|
|
995
|
+
}
|
|
693
996
|
function accentBandContext(brand) {
|
|
694
997
|
const p = brand.palette;
|
|
695
998
|
const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
|
|
@@ -789,12 +1092,10 @@ function ButtonEl({
|
|
|
789
1092
|
width: fullWidth ? "100%" : void 0,
|
|
790
1093
|
alignItems: "center",
|
|
791
1094
|
justifyContent: "center",
|
|
792
|
-
padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
|
|
793
|
-
borderRadius: AI_TREE_TOKENS.radiusButton,
|
|
794
1095
|
textDecoration: "none",
|
|
795
1096
|
cursor: "pointer",
|
|
796
|
-
...
|
|
797
|
-
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ??
|
|
1097
|
+
...buttonShellStyle(ctx, fullWidth),
|
|
1098
|
+
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
|
|
798
1099
|
},
|
|
799
1100
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
|
|
800
1101
|
}
|
|
@@ -1834,15 +2135,12 @@ function renderNode(node, ctx, path) {
|
|
|
1834
2135
|
alignSelf: submitAlign,
|
|
1835
2136
|
border: "none",
|
|
1836
2137
|
cursor: "pointer",
|
|
1837
|
-
padding
|
|
1838
|
-
|
|
1839
|
-
// CTA); 8px only when the page has no template button to match.
|
|
1840
|
-
borderRadius: ctx.buttonRadius ?? 8,
|
|
2138
|
+
// Shape/padding/typography follow the host template's own buttons.
|
|
2139
|
+
...buttonShellStyle(ctx),
|
|
1841
2140
|
// Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
|
|
1842
2141
|
// reads correctly on custom palettes.
|
|
1843
2142
|
background: ctx.brand.palette.primary,
|
|
1844
|
-
color: ctx.buttonLabel ?? ctx.brand
|
|
1845
|
-
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
2143
|
+
color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
|
|
1846
2144
|
},
|
|
1847
2145
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
|
|
1848
2146
|
},
|
|
@@ -1877,7 +2175,7 @@ function renderNode(node, ctx, path) {
|
|
|
1877
2175
|
function AiTreeRenderer({
|
|
1878
2176
|
tree,
|
|
1879
2177
|
brand,
|
|
1880
|
-
|
|
2178
|
+
buttonStyle,
|
|
1881
2179
|
resolveMedia,
|
|
1882
2180
|
editKeyPrefix
|
|
1883
2181
|
}) {
|
|
@@ -1893,7 +2191,7 @@ function AiTreeRenderer({
|
|
|
1893
2191
|
cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
|
|
1894
2192
|
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
|
|
1895
2193
|
sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
|
|
1896
|
-
|
|
2194
|
+
buttonStyle,
|
|
1897
2195
|
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
1898
2196
|
};
|
|
1899
2197
|
const settings = tree.settings ?? {};
|
|
@@ -1999,7 +2297,7 @@ function AiTreeRenderer({
|
|
|
1999
2297
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
2000
2298
|
var CONTAINER_ATTR = "data-ohw-ai-generated";
|
|
2001
2299
|
var REPLACED_ATTR = "data-ohw-ai-replaced-by";
|
|
2002
|
-
var
|
|
2300
|
+
var REMOVED_ATTR2 = "data-ohw-ai-removed";
|
|
2003
2301
|
var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
|
|
2004
2302
|
var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
2005
2303
|
function readRootVar(name) {
|
|
@@ -2023,13 +2321,13 @@ function deriveBrandOverride() {
|
|
|
2023
2321
|
};
|
|
2024
2322
|
}
|
|
2025
2323
|
function deriveTemplateBrand() {
|
|
2026
|
-
const
|
|
2027
|
-
const
|
|
2028
|
-
const light = readRootVar("--color-light");
|
|
2324
|
+
const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
|
|
2325
|
+
const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
|
|
2326
|
+
const light = readRootVar("--brand-background") || readRootVar("--color-light");
|
|
2029
2327
|
if (!dark || !primary || !light) return null;
|
|
2030
|
-
const accent = readRootVar("--color-accent");
|
|
2031
|
-
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
2032
|
-
const body = readRootVar("--font-body");
|
|
2328
|
+
const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
|
|
2329
|
+
const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
|
|
2330
|
+
const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
|
|
2033
2331
|
return {
|
|
2034
2332
|
palette: { dark, primary, accent: accent || dark, light },
|
|
2035
2333
|
fonts: {
|
|
@@ -2038,12 +2336,32 @@ function deriveTemplateBrand() {
|
|
|
2038
2336
|
}
|
|
2039
2337
|
};
|
|
2040
2338
|
}
|
|
2041
|
-
function
|
|
2339
|
+
function deriveTemplateButtonStyle() {
|
|
2042
2340
|
if (typeof document === "undefined") return null;
|
|
2043
|
-
const btn = document.
|
|
2341
|
+
const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
|
|
2342
|
+
(el) => !el.closest(`[${CONTAINER_ATTR}]`)
|
|
2343
|
+
);
|
|
2044
2344
|
if (!btn) return null;
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2345
|
+
const cs = getComputedStyle(btn);
|
|
2346
|
+
const corners = [
|
|
2347
|
+
cs.borderTopLeftRadius,
|
|
2348
|
+
cs.borderTopRightRadius,
|
|
2349
|
+
cs.borderBottomRightRadius,
|
|
2350
|
+
cs.borderBottomLeftRadius
|
|
2351
|
+
].map((v) => v || "0px");
|
|
2352
|
+
const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
|
|
2353
|
+
const px = (v) => parseFloat(v) || 0;
|
|
2354
|
+
const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom));
|
|
2355
|
+
const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
|
|
2356
|
+
return {
|
|
2357
|
+
radius: radius || "10px",
|
|
2358
|
+
padding: `${padY}px ${padX}px`,
|
|
2359
|
+
fontFamily: cs.fontFamily || "",
|
|
2360
|
+
fontSize: cs.fontSize || "",
|
|
2361
|
+
fontWeight: cs.fontWeight || "",
|
|
2362
|
+
letterSpacing: cs.letterSpacing || "",
|
|
2363
|
+
textTransform: cs.textTransform || ""
|
|
2364
|
+
};
|
|
2047
2365
|
}
|
|
2048
2366
|
var mounted = /* @__PURE__ */ new Map();
|
|
2049
2367
|
function findTemplateSection(id) {
|
|
@@ -2094,18 +2412,18 @@ function placeContainer(container, entry) {
|
|
|
2094
2412
|
}
|
|
2095
2413
|
function syncRemovedSections(state) {
|
|
2096
2414
|
const removed = new Set(state.removed ?? []);
|
|
2097
|
-
for (const el of document.querySelectorAll(`[${
|
|
2415
|
+
for (const el of document.querySelectorAll(`[${REMOVED_ATTR2}]`)) {
|
|
2098
2416
|
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
2099
2417
|
if (!removed.has(id)) {
|
|
2100
2418
|
el.style.removeProperty("display");
|
|
2101
|
-
el.removeAttribute(
|
|
2419
|
+
el.removeAttribute(REMOVED_ATTR2);
|
|
2102
2420
|
}
|
|
2103
2421
|
}
|
|
2104
2422
|
for (const id of removed) {
|
|
2105
2423
|
const section = findTemplateSection(id);
|
|
2106
2424
|
if (section && !section.hasAttribute(REPLACED_ATTR)) {
|
|
2107
2425
|
section.style.display = "none";
|
|
2108
|
-
section.setAttribute(
|
|
2426
|
+
section.setAttribute(REMOVED_ATTR2, "");
|
|
2109
2427
|
}
|
|
2110
2428
|
}
|
|
2111
2429
|
}
|
|
@@ -2122,7 +2440,7 @@ function syncTemplateHidden(state, pageHasSections) {
|
|
|
2122
2440
|
if (el.hasAttribute(CONTAINER_ATTR)) continue;
|
|
2123
2441
|
if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
|
|
2124
2442
|
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
2125
|
-
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(
|
|
2443
|
+
if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR2)) continue;
|
|
2126
2444
|
el.style.display = "none";
|
|
2127
2445
|
el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
|
|
2128
2446
|
}
|
|
@@ -2146,18 +2464,23 @@ function syncReplacedOriginals(state) {
|
|
|
2146
2464
|
}
|
|
2147
2465
|
}
|
|
2148
2466
|
var sectionOrderIndex = /* @__PURE__ */ new Map();
|
|
2467
|
+
var removedSectionIds = /* @__PURE__ */ new Set();
|
|
2149
2468
|
function setAiSectionOrder(raw, currentPath) {
|
|
2150
2469
|
const next = /* @__PURE__ */ new Map();
|
|
2470
|
+
const removed = /* @__PURE__ */ new Set();
|
|
2151
2471
|
if (raw) {
|
|
2152
2472
|
try {
|
|
2153
2473
|
const entries = JSON.parse(raw);
|
|
2154
2474
|
for (const entry of entries) {
|
|
2155
|
-
if (
|
|
2475
|
+
if (entry.pagePath && entry.pagePath !== currentPath) continue;
|
|
2476
|
+
next.set(entry.instanceId, entry.order);
|
|
2477
|
+
if (entry.removed) removed.add(entry.instanceId);
|
|
2156
2478
|
}
|
|
2157
2479
|
} catch {
|
|
2158
2480
|
}
|
|
2159
2481
|
}
|
|
2160
2482
|
sectionOrderIndex = next;
|
|
2483
|
+
removedSectionIds = removed;
|
|
2161
2484
|
}
|
|
2162
2485
|
function applyExplicitOrder(entries) {
|
|
2163
2486
|
if (sectionOrderIndex.size === 0) return entries;
|
|
@@ -2193,11 +2516,23 @@ function orderByChain(sections) {
|
|
|
2193
2516
|
for (const root of roots) visit(root);
|
|
2194
2517
|
return out.length === sections.length ? out : sections;
|
|
2195
2518
|
}
|
|
2519
|
+
function syncSoftRemovedGenerated() {
|
|
2520
|
+
for (const [id, section] of mounted) {
|
|
2521
|
+
const el = section.container;
|
|
2522
|
+
if (removedSectionIds.has(id)) {
|
|
2523
|
+
el.style.display = "none";
|
|
2524
|
+
el.setAttribute(REMOVED_ATTR, "");
|
|
2525
|
+
} else if (el.hasAttribute(REMOVED_ATTR)) {
|
|
2526
|
+
el.style.removeProperty("display");
|
|
2527
|
+
el.removeAttribute(REMOVED_ATTR);
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2196
2531
|
function applyAiSectionsToDom(state, options) {
|
|
2197
2532
|
if (typeof document === "undefined") return;
|
|
2198
2533
|
const brandOverride = deriveBrandOverride();
|
|
2199
2534
|
const templateBrand = deriveTemplateBrand();
|
|
2200
|
-
const
|
|
2535
|
+
const templateButtonStyle = deriveTemplateButtonStyle();
|
|
2201
2536
|
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2202
2537
|
const pagePath = window.location.pathname;
|
|
2203
2538
|
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
@@ -2237,7 +2572,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2237
2572
|
{
|
|
2238
2573
|
tree: entry.tree,
|
|
2239
2574
|
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2240
|
-
|
|
2575
|
+
buttonStyle: templateButtonStyle,
|
|
2241
2576
|
resolveMedia,
|
|
2242
2577
|
editKeyPrefix: `ai.${entry.id}`
|
|
2243
2578
|
}
|
|
@@ -2260,6 +2595,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2260
2595
|
syncReplacedOriginals(state);
|
|
2261
2596
|
syncRemovedSections(state);
|
|
2262
2597
|
syncTemplateHidden(state, pageSections.length > 0);
|
|
2598
|
+
syncSoftRemovedGenerated();
|
|
2263
2599
|
}
|
|
2264
2600
|
function unmountAllAiSections() {
|
|
2265
2601
|
for (const [, section] of mounted) {
|
|
@@ -2272,9 +2608,9 @@ function unmountAllAiSections() {
|
|
|
2272
2608
|
el.style.removeProperty("display");
|
|
2273
2609
|
el.removeAttribute(REPLACED_ATTR);
|
|
2274
2610
|
}
|
|
2275
|
-
for (const el of document.querySelectorAll(`[${
|
|
2611
|
+
for (const el of document.querySelectorAll(`[${REMOVED_ATTR2}]`)) {
|
|
2276
2612
|
el.style.removeProperty("display");
|
|
2277
|
-
el.removeAttribute(
|
|
2613
|
+
el.removeAttribute(REMOVED_ATTR2);
|
|
2278
2614
|
}
|
|
2279
2615
|
}
|
|
2280
2616
|
|
|
@@ -8235,240 +8571,6 @@ function CarouselOverlay({
|
|
|
8235
8571
|
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
8236
8572
|
var import_react8 = require("react");
|
|
8237
8573
|
var import_lucide_react7 = require("lucide-react");
|
|
8238
|
-
|
|
8239
|
-
// src/lib/sections.ts
|
|
8240
|
-
var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
|
|
8241
|
-
function isChromeSection(el) {
|
|
8242
|
-
return el.matches("header, nav, footer, aside");
|
|
8243
|
-
}
|
|
8244
|
-
function titleCaseSectionId(id) {
|
|
8245
|
-
return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
8246
|
-
}
|
|
8247
|
-
function parseSectionsFromRoot(root) {
|
|
8248
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8249
|
-
const sections = [];
|
|
8250
|
-
for (const el of root.querySelectorAll("[data-ohw-section]")) {
|
|
8251
|
-
const id = el.getAttribute("data-ohw-section") ?? "";
|
|
8252
|
-
if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
|
|
8253
|
-
if (el.parentElement?.closest("[data-ohw-section]")) continue;
|
|
8254
|
-
if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
|
|
8255
|
-
continue;
|
|
8256
|
-
seen.add(id);
|
|
8257
|
-
const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
|
|
8258
|
-
sections.push({ id, label });
|
|
8259
|
-
}
|
|
8260
|
-
return sections;
|
|
8261
|
-
}
|
|
8262
|
-
function collectSectionsFromDom() {
|
|
8263
|
-
if (typeof document === "undefined") return [];
|
|
8264
|
-
return parseSectionsFromRoot(document);
|
|
8265
|
-
}
|
|
8266
|
-
function parseSectionsFromHtml(html) {
|
|
8267
|
-
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
8268
|
-
return parseSectionsFromRoot(doc);
|
|
8269
|
-
}
|
|
8270
|
-
|
|
8271
|
-
// src/lib/section-instances.ts
|
|
8272
|
-
var SECTION_ORDER_KEY = "__ohw_section_order";
|
|
8273
|
-
var REMOVED_ATTR2 = "data-ohw-section-removed";
|
|
8274
|
-
function isRemovedSection(el) {
|
|
8275
|
-
return el.hasAttribute(REMOVED_ATTR2);
|
|
8276
|
-
}
|
|
8277
|
-
function movableUnit(el) {
|
|
8278
|
-
return el.closest("[data-ohw-section-container]") ?? el;
|
|
8279
|
-
}
|
|
8280
|
-
function sectionTypeOf(el) {
|
|
8281
|
-
return el.getAttribute("data-ohw-section") ?? el.querySelector("[data-ohw-section]")?.getAttribute("data-ohw-section") ?? "";
|
|
8282
|
-
}
|
|
8283
|
-
function sectionElementOf(el) {
|
|
8284
|
-
return el.hasAttribute("data-ohw-section") ? el : el.querySelector("[data-ohw-section]") ?? el;
|
|
8285
|
-
}
|
|
8286
|
-
function collectTopLevelUnits(predicate) {
|
|
8287
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8288
|
-
const result = [];
|
|
8289
|
-
document.querySelectorAll("[data-ohw-section]").forEach((el) => {
|
|
8290
|
-
if (!predicate(el)) return;
|
|
8291
|
-
const unit = movableUnit(el);
|
|
8292
|
-
if (unit.parentElement?.closest("[data-ohw-section],[data-ohw-section-container]")) return;
|
|
8293
|
-
if (seen.has(unit)) return;
|
|
8294
|
-
seen.add(unit);
|
|
8295
|
-
result.push(unit);
|
|
8296
|
-
});
|
|
8297
|
-
return result;
|
|
8298
|
-
}
|
|
8299
|
-
function topLevelSections() {
|
|
8300
|
-
return collectTopLevelUnits((el) => !isChromeSection(el) && !isRemovedSection(movableUnit(el)));
|
|
8301
|
-
}
|
|
8302
|
-
function instanceIdOf(el) {
|
|
8303
|
-
return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
|
|
8304
|
-
}
|
|
8305
|
-
function findByInstanceId(instanceId) {
|
|
8306
|
-
const escapedId = CSS.escape(instanceId);
|
|
8307
|
-
const direct = document.querySelector(`[data-ohw-instance="${escapedId}"]`);
|
|
8308
|
-
if (direct) return movableUnit(direct);
|
|
8309
|
-
const bare = document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
|
|
8310
|
-
return bare ? movableUnit(bare) : null;
|
|
8311
|
-
}
|
|
8312
|
-
function planSectionMove(instanceId, targetIndex, currentPath) {
|
|
8313
|
-
const sections = topLevelSections();
|
|
8314
|
-
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
8315
|
-
if (index === -1) return null;
|
|
8316
|
-
const dragged = sections[index];
|
|
8317
|
-
const others = sections.filter((_, i) => i !== index);
|
|
8318
|
-
const clamped = Math.max(0, Math.min(targetIndex, others.length));
|
|
8319
|
-
const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
|
|
8320
|
-
return reordered.map((el, order) => ({
|
|
8321
|
-
instanceId: instanceIdOf(el),
|
|
8322
|
-
type: sectionTypeOf(el),
|
|
8323
|
-
order,
|
|
8324
|
-
pagePath: currentPath
|
|
8325
|
-
}));
|
|
8326
|
-
}
|
|
8327
|
-
function moveSectionInstance(instanceId, direction, currentPath) {
|
|
8328
|
-
const sections = topLevelSections();
|
|
8329
|
-
const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
|
|
8330
|
-
if (index === -1) return null;
|
|
8331
|
-
const siblingIndex = direction === "up" ? index - 1 : index + 1;
|
|
8332
|
-
if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
|
|
8333
|
-
const entries = planSectionMove(instanceId, siblingIndex, currentPath);
|
|
8334
|
-
if (!entries) return null;
|
|
8335
|
-
applyPersistedOrder(entries);
|
|
8336
|
-
return entries;
|
|
8337
|
-
}
|
|
8338
|
-
function syncRemovedFlags(entries) {
|
|
8339
|
-
const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
|
|
8340
|
-
document.querySelectorAll(`[${REMOVED_ATTR2}]`).forEach((el) => {
|
|
8341
|
-
if (!removedIds.has(instanceIdOf(el))) {
|
|
8342
|
-
el.style.removeProperty("display");
|
|
8343
|
-
el.removeAttribute(REMOVED_ATTR2);
|
|
8344
|
-
}
|
|
8345
|
-
});
|
|
8346
|
-
for (const id of removedIds) {
|
|
8347
|
-
const el = findByInstanceId(id);
|
|
8348
|
-
if (el) {
|
|
8349
|
-
el.style.display = "none";
|
|
8350
|
-
el.setAttribute(REMOVED_ATTR2, "");
|
|
8351
|
-
}
|
|
8352
|
-
}
|
|
8353
|
-
}
|
|
8354
|
-
function applyPersistedOrder(entries) {
|
|
8355
|
-
syncRemovedFlags(entries);
|
|
8356
|
-
if (entries.length === 0) return;
|
|
8357
|
-
const sections = topLevelSections();
|
|
8358
|
-
if (sections.length === 0) return;
|
|
8359
|
-
const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
|
|
8360
|
-
const ordered = [...sections].sort((a, b) => {
|
|
8361
|
-
const aOrder = orderIndex.get(instanceIdOf(a));
|
|
8362
|
-
const bOrder = orderIndex.get(instanceIdOf(b));
|
|
8363
|
-
if (aOrder === void 0 && bOrder === void 0) return 0;
|
|
8364
|
-
if (aOrder === void 0) return 1;
|
|
8365
|
-
if (bOrder === void 0) return -1;
|
|
8366
|
-
return aOrder - bOrder;
|
|
8367
|
-
});
|
|
8368
|
-
let prev = null;
|
|
8369
|
-
for (const el of ordered) {
|
|
8370
|
-
if (prev) prev.after(el);
|
|
8371
|
-
prev = el;
|
|
8372
|
-
}
|
|
8373
|
-
}
|
|
8374
|
-
function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
|
|
8375
|
-
if (!findByInstanceId(instanceId)) return null;
|
|
8376
|
-
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
8377
|
-
const allSections = collectTopLevelUnits((el) => !isChromeSection(el));
|
|
8378
|
-
allSections.forEach((el, order) => {
|
|
8379
|
-
const id = instanceIdOf(el);
|
|
8380
|
-
if (!byId.has(id)) {
|
|
8381
|
-
byId.set(id, { instanceId: id, type: sectionTypeOf(el), order, pagePath: currentPath });
|
|
8382
|
-
}
|
|
8383
|
-
});
|
|
8384
|
-
const target = byId.get(instanceId);
|
|
8385
|
-
if (!target) return null;
|
|
8386
|
-
byId.set(instanceId, { ...target, removed });
|
|
8387
|
-
const entries = Array.from(byId.values());
|
|
8388
|
-
applyPersistedOrder(entries);
|
|
8389
|
-
return entries;
|
|
8390
|
-
}
|
|
8391
|
-
function deleteSectionInstance(instanceId, currentPath, existingEntries) {
|
|
8392
|
-
return setSectionRemoved(instanceId, currentPath, existingEntries, true);
|
|
8393
|
-
}
|
|
8394
|
-
function restoreSectionInstance(instanceId, currentPath, existingEntries) {
|
|
8395
|
-
return setSectionRemoved(instanceId, currentPath, existingEntries, false);
|
|
8396
|
-
}
|
|
8397
|
-
function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
|
|
8398
|
-
const original = findByInstanceId(instanceId);
|
|
8399
|
-
if (!original) return null;
|
|
8400
|
-
const clone = original.cloneNode(true);
|
|
8401
|
-
clone.setAttribute("data-ohw-instance", newId);
|
|
8402
|
-
const keyRekeys = rekeySectionSubtree(clone, newId);
|
|
8403
|
-
original.insertAdjacentElement("afterend", clone);
|
|
8404
|
-
const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
|
|
8405
|
-
const entries = topLevelSections().map((el, order) => {
|
|
8406
|
-
const id = instanceIdOf(el);
|
|
8407
|
-
return {
|
|
8408
|
-
instanceId: id,
|
|
8409
|
-
type: sectionTypeOf(el),
|
|
8410
|
-
order,
|
|
8411
|
-
pagePath: currentPath,
|
|
8412
|
-
...byId.get(id)?.removed ? { removed: true } : {}
|
|
8413
|
-
};
|
|
8414
|
-
});
|
|
8415
|
-
applyPersistedOrder(entries);
|
|
8416
|
-
return { entries, keyRekeys };
|
|
8417
|
-
}
|
|
8418
|
-
function newInstanceId() {
|
|
8419
|
-
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
8420
|
-
}
|
|
8421
|
-
function getPageSectionOrderEntries(raw, currentPath) {
|
|
8422
|
-
if (!raw) return [];
|
|
8423
|
-
try {
|
|
8424
|
-
const entries = JSON.parse(raw);
|
|
8425
|
-
return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
|
|
8426
|
-
} catch {
|
|
8427
|
-
return [];
|
|
8428
|
-
}
|
|
8429
|
-
}
|
|
8430
|
-
function rekeySectionSubtree(root, instanceId) {
|
|
8431
|
-
const suffix = `::${instanceId}`;
|
|
8432
|
-
const pairs = [];
|
|
8433
|
-
const rekey = (el, attr) => {
|
|
8434
|
-
const current = el.getAttribute(attr);
|
|
8435
|
-
if (!current) return;
|
|
8436
|
-
const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
|
|
8437
|
-
const next = `${base}${suffix}`;
|
|
8438
|
-
el.setAttribute(attr, next);
|
|
8439
|
-
pairs.push({ from: current, to: next });
|
|
8440
|
-
};
|
|
8441
|
-
if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
|
|
8442
|
-
if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
|
|
8443
|
-
root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
|
|
8444
|
-
root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
|
|
8445
|
-
return pairs;
|
|
8446
|
-
}
|
|
8447
|
-
function initSectionInstancesFromContent(content, currentPath) {
|
|
8448
|
-
document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
|
|
8449
|
-
el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
|
|
8450
|
-
});
|
|
8451
|
-
document.querySelectorAll("[data-ohw-section-container]:not([data-ohw-instance])").forEach((el) => {
|
|
8452
|
-
const type = sectionTypeOf(el);
|
|
8453
|
-
if (type) el.setAttribute("data-ohw-instance", type);
|
|
8454
|
-
});
|
|
8455
|
-
const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
|
|
8456
|
-
for (const entry of entries) {
|
|
8457
|
-
if (entry.instanceId === entry.type) continue;
|
|
8458
|
-
if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
|
|
8459
|
-
const original = document.querySelector(
|
|
8460
|
-
`[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
|
|
8461
|
-
);
|
|
8462
|
-
if (!original) continue;
|
|
8463
|
-
const clone = original.cloneNode(true);
|
|
8464
|
-
clone.setAttribute("data-ohw-instance", entry.instanceId);
|
|
8465
|
-
rekeySectionSubtree(clone, entry.instanceId);
|
|
8466
|
-
original.insertAdjacentElement("afterend", clone);
|
|
8467
|
-
}
|
|
8468
|
-
applyPersistedOrder(entries);
|
|
8469
|
-
}
|
|
8470
|
-
|
|
8471
|
-
// src/ui/ai-section/AiSectionOverlay.tsx
|
|
8472
8574
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
8473
8575
|
var findSectionElement = findByInstanceId;
|
|
8474
8576
|
function readRect(instanceId) {
|
|
@@ -14251,15 +14353,17 @@ function useSectionDrag({
|
|
|
14251
14353
|
clearSectionDragVisuals();
|
|
14252
14354
|
return;
|
|
14253
14355
|
}
|
|
14254
|
-
const orderJson = JSON.stringify(
|
|
14356
|
+
const orderJson = JSON.stringify(
|
|
14357
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
14358
|
+
);
|
|
14255
14359
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
14256
14360
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
14257
14361
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
14258
|
-
applyPersistedOrder(
|
|
14362
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
14259
14363
|
clearSectionDragVisuals();
|
|
14260
14364
|
requestAnimationFrame(() => {
|
|
14261
14365
|
if (editContentRef.current[SECTION_ORDER_KEY] === orderJson) {
|
|
14262
|
-
applyPersistedOrder(
|
|
14366
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
14263
14367
|
}
|
|
14264
14368
|
requestAnimationFrame(() => {
|
|
14265
14369
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20284,6 +20388,44 @@ function OhhwellsBridge() {
|
|
|
20284
20388
|
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
20285
20389
|
}, 400));
|
|
20286
20390
|
};
|
|
20391
|
+
const reapCommittedAiSections = (excludeIds) => {
|
|
20392
|
+
const aiState = parseAiSectionsState(aiSectionsRef.current);
|
|
20393
|
+
if (aiState.sections.length === 0) return [];
|
|
20394
|
+
let orderEntries = [];
|
|
20395
|
+
try {
|
|
20396
|
+
const parsed = JSON.parse(editContentRef.current[SECTION_ORDER_KEY] ?? "[]");
|
|
20397
|
+
if (Array.isArray(parsed)) orderEntries = parsed;
|
|
20398
|
+
} catch {
|
|
20399
|
+
return [];
|
|
20400
|
+
}
|
|
20401
|
+
const removedIds = orderEntries.filter((entry) => entry && typeof entry.instanceId === "string" && entry.removed).map((entry) => entry.instanceId);
|
|
20402
|
+
if (removedIds.length === 0) return [];
|
|
20403
|
+
const result = reapRemovedAiSections(aiState, parseStyleStore(stylesRef.current), removedIds, excludeIds);
|
|
20404
|
+
if (!result.changed) return [];
|
|
20405
|
+
const nodes = [];
|
|
20406
|
+
aiSectionsRef.current = serializeAiSectionsState(result.state);
|
|
20407
|
+
nodes.push({ key: AI_SECTIONS_KEY, text: aiSectionsRef.current });
|
|
20408
|
+
const reaped = new Set(result.reapedIds);
|
|
20409
|
+
const nextOrderJson = JSON.stringify(orderEntries.filter((entry) => !reaped.has(entry.instanceId)));
|
|
20410
|
+
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: nextOrderJson };
|
|
20411
|
+
setAiSectionOrder(nextOrderJson, window.location.pathname);
|
|
20412
|
+
nodes.push({ key: SECTION_ORDER_KEY, text: nextOrderJson });
|
|
20413
|
+
if (result.store) {
|
|
20414
|
+
stylesRef.current = JSON.stringify(result.store);
|
|
20415
|
+
nodes.push({ key: STYLE_STORE_KEY, text: stylesRef.current });
|
|
20416
|
+
}
|
|
20417
|
+
const nextContent = { ...editContentRef.current };
|
|
20418
|
+
for (const key of Object.keys(nextContent)) {
|
|
20419
|
+
if (key.startsWith(AI_SLOT_KEY_PREFIX) && result.slotPrefixes.some((prefix) => key.startsWith(prefix)) && nextContent[key] !== "") {
|
|
20420
|
+
nextContent[key] = "";
|
|
20421
|
+
nodes.push({ key, text: "" });
|
|
20422
|
+
}
|
|
20423
|
+
}
|
|
20424
|
+
editContentRef.current = nextContent;
|
|
20425
|
+
applyAiSectionsToDom(result.state);
|
|
20426
|
+
applyStylesToDom(parseStyleStore(stylesRef.current));
|
|
20427
|
+
return nodes;
|
|
20428
|
+
};
|
|
20287
20429
|
const handleHydrate = (e) => {
|
|
20288
20430
|
if (e.data?.type !== "ow:hydrate") return;
|
|
20289
20431
|
const content = e.data.content;
|
|
@@ -20352,6 +20494,11 @@ function OhhwellsBridge() {
|
|
|
20352
20494
|
reconcileFooterOrderFromContent(editContentRef.current);
|
|
20353
20495
|
syncNavigationDragCursorAttrs();
|
|
20354
20496
|
enforceLinkHrefs();
|
|
20497
|
+
const hydrateReapExclude = /* @__PURE__ */ new Set();
|
|
20498
|
+
const hydratePendingUndo = pendingDeleteUndoRef.current;
|
|
20499
|
+
if (hydratePendingUndo?.sectionInstanceId) hydrateReapExclude.add(hydratePendingUndo.sectionInstanceId);
|
|
20500
|
+
const reapNodes = reapCommittedAiSections(hydrateReapExclude);
|
|
20501
|
+
if (reapNodes.length > 0) postToParentRef.current({ type: "ow:change", nodes: reapNodes });
|
|
20355
20502
|
const hydratedHeight = document.body.scrollHeight;
|
|
20356
20503
|
if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
|
|
20357
20504
|
postToParentRef.current({ type: "ow:hydrate-done" });
|
|
@@ -20536,8 +20683,11 @@ function OhhwellsBridge() {
|
|
|
20536
20683
|
if (!instanceId || !direction) return;
|
|
20537
20684
|
const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
|
|
20538
20685
|
if (!entries) return;
|
|
20539
|
-
const orderJson = JSON.stringify(
|
|
20686
|
+
const orderJson = JSON.stringify(
|
|
20687
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
20688
|
+
);
|
|
20540
20689
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20690
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
20541
20691
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20542
20692
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
20543
20693
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20583,8 +20733,11 @@ function OhhwellsBridge() {
|
|
|
20583
20733
|
const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
|
|
20584
20734
|
const entries = deleteSectionInstance(instanceId, window.location.pathname, currentEntries);
|
|
20585
20735
|
if (!entries) return;
|
|
20586
|
-
const orderJson = JSON.stringify(
|
|
20736
|
+
const orderJson = JSON.stringify(
|
|
20737
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
20738
|
+
);
|
|
20587
20739
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
|
|
20740
|
+
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20588
20741
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
|
|
20589
20742
|
aiSectionApiRef.current?.clear();
|
|
20590
20743
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20593,6 +20746,7 @@ function OhhwellsBridge() {
|
|
|
20593
20746
|
const actionId = newInstanceId();
|
|
20594
20747
|
pendingDeleteUndoRef.current = {
|
|
20595
20748
|
actionId,
|
|
20749
|
+
sectionInstanceId: instanceId,
|
|
20596
20750
|
restore: () => {
|
|
20597
20751
|
const restoredEntries = getPageSectionOrderEntries(
|
|
20598
20752
|
editContentRef.current[SECTION_ORDER_KEY],
|
|
@@ -20600,8 +20754,11 @@ function OhhwellsBridge() {
|
|
|
20600
20754
|
);
|
|
20601
20755
|
const restored = restoreSectionInstance(instanceId, window.location.pathname, restoredEntries);
|
|
20602
20756
|
if (!restored) return;
|
|
20603
|
-
const restoredJson = JSON.stringify(
|
|
20757
|
+
const restoredJson = JSON.stringify(
|
|
20758
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, restored)
|
|
20759
|
+
);
|
|
20604
20760
|
editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
|
|
20761
|
+
setAiSectionOrder(restoredJson, window.location.pathname);
|
|
20605
20762
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
|
|
20606
20763
|
window.dispatchEvent(new Event("resize"));
|
|
20607
20764
|
const restoreHeight = document.body.scrollHeight;
|
|
@@ -20627,7 +20784,9 @@ function OhhwellsBridge() {
|
|
|
20627
20784
|
const result = duplicateSectionInstance(instanceId, newId, window.location.pathname, currentEntries);
|
|
20628
20785
|
if (!result) return;
|
|
20629
20786
|
const { entries, keyRekeys } = result;
|
|
20630
|
-
const orderJson = JSON.stringify(
|
|
20787
|
+
const orderJson = JSON.stringify(
|
|
20788
|
+
mergePageSectionOrder(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname, entries)
|
|
20789
|
+
);
|
|
20631
20790
|
const nodes = [{ key: SECTION_ORDER_KEY, text: orderJson }];
|
|
20632
20791
|
for (const { from, to } of keyRekeys) {
|
|
20633
20792
|
const inherited = editContentRef.current[from];
|
|
@@ -20637,6 +20796,7 @@ function OhhwellsBridge() {
|
|
|
20637
20796
|
...editContentRef.current,
|
|
20638
20797
|
...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
|
|
20639
20798
|
};
|
|
20799
|
+
applyPersistedOrder(getPageSectionOrderEntries(orderJson, window.location.pathname));
|
|
20640
20800
|
setAiSectionOrder(orderJson, window.location.pathname);
|
|
20641
20801
|
postToParentRef.current({ type: "ow:change", nodes });
|
|
20642
20802
|
window.dispatchEvent(new Event("resize"));
|
|
@@ -20900,6 +21060,10 @@ function OhhwellsBridge() {
|
|
|
20900
21060
|
};
|
|
20901
21061
|
const handleSave = (e) => {
|
|
20902
21062
|
if (e.data?.type !== "ow:save") return;
|
|
21063
|
+
const pendingUndo = pendingDeleteUndoRef.current;
|
|
21064
|
+
const reapExclude = /* @__PURE__ */ new Set();
|
|
21065
|
+
if (pendingUndo?.sectionInstanceId) reapExclude.add(pendingUndo.sectionInstanceId);
|
|
21066
|
+
const reapNodes = reapCommittedAiSections(reapExclude);
|
|
20903
21067
|
const nodes = collectEditableNodes(editContentRef.current);
|
|
20904
21068
|
const tracker = document.querySelector("[data-ohw-sections-tracker]");
|
|
20905
21069
|
if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
|
|
@@ -20919,6 +21083,11 @@ function OhhwellsBridge() {
|
|
|
20919
21083
|
const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
|
|
20920
21084
|
if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
|
|
20921
21085
|
});
|
|
21086
|
+
for (const reapNode of reapNodes) {
|
|
21087
|
+
if (reapNode.key.startsWith(AI_SLOT_KEY_PREFIX) && !nodes.some((n) => n.key === reapNode.key)) {
|
|
21088
|
+
nodes.push({ key: reapNode.key, type: "text", text: reapNode.text });
|
|
21089
|
+
}
|
|
21090
|
+
}
|
|
20922
21091
|
postToParentRef.current({ type: "ow:save-result", nodes });
|
|
20923
21092
|
};
|
|
20924
21093
|
const handleInsertSection = (e) => {
|
|
@@ -21540,7 +21709,7 @@ function OhhwellsBridge() {
|
|
|
21540
21709
|
postToParent2({
|
|
21541
21710
|
type: "ow:ready",
|
|
21542
21711
|
version: "1",
|
|
21543
|
-
bridgeVersion: "0.1.
|
|
21712
|
+
bridgeVersion: "0.1.95",
|
|
21544
21713
|
path: pathname,
|
|
21545
21714
|
nodes: collectEditableNodes(editContentRef.current),
|
|
21546
21715
|
sections
|