@ohhwells/bridge 0.1.85-next.254 → 0.1.86-next.255

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 CHANGED
@@ -144,6 +144,9 @@ function isRenderableTree(value) {
144
144
  // src/lib/ai-sections-store.ts
145
145
  var AI_SECTIONS_KEY = "__ohw_ai_sections";
146
146
  var AI_SLOT_KEY_PREFIX = "ai.";
147
+ function aiSlotKeyPrefixFor(sectionId) {
148
+ return `${AI_SLOT_KEY_PREFIX}${sectionId}.`;
149
+ }
147
150
  var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
148
151
  function parseAiSectionsState(raw) {
149
152
  if (!raw) return EMPTY_AI_SECTIONS;
@@ -255,6 +258,33 @@ function deleteSectionFromState(state, sectionId) {
255
258
  if (removed.includes(sectionId)) return state;
256
259
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
257
260
  }
261
+ function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE__ */ new Set()) {
262
+ const generated = new Set(state.sections.map((entry) => entry.id));
263
+ const reapedIds = [...new Set(removedIds)].filter((id) => generated.has(id) && !excludeIds.has(id));
264
+ if (reapedIds.length === 0) {
265
+ return { state, store, reapedIds: [], slotPrefixes: [], changed: false };
266
+ }
267
+ const reaped = new Set(reapedIds);
268
+ const slotPrefixes = reapedIds.map(aiSlotKeyPrefixFor);
269
+ const nextState = {
270
+ ...state,
271
+ v: 1,
272
+ sections: state.sections.filter((entry) => !reaped.has(entry.id))
273
+ };
274
+ let nextStore = store;
275
+ if (store) {
276
+ const sections = {};
277
+ for (const [key, override] of Object.entries(store.sections)) {
278
+ if (!reaped.has(key)) sections[key] = override;
279
+ }
280
+ const nodes = {};
281
+ for (const [key, override] of Object.entries(store.nodes)) {
282
+ if (!slotPrefixes.some((prefix) => key.startsWith(prefix))) nodes[key] = override;
283
+ }
284
+ nextStore = { v: 1, sections, nodes };
285
+ }
286
+ return { state: nextState, store: nextStore, reapedIds, slotPrefixes, changed: true };
287
+ }
258
288
 
259
289
  // src/lib/brand-chrome.ts
260
290
  var BRAND_NAME_KEY = "__ohw_brand_name";
@@ -637,6 +667,213 @@ function applyStylesToDom(store) {
637
667
  var import_react_dom = require("react-dom");
638
668
  var import_client = require("react-dom/client");
639
669
 
670
+ // src/lib/sections.ts
671
+ var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
672
+ function isChromeSection(el) {
673
+ return el.matches("header, nav, footer, aside");
674
+ }
675
+ function titleCaseSectionId(id) {
676
+ return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
677
+ }
678
+ function parseSectionsFromRoot(root) {
679
+ const seen = /* @__PURE__ */ new Set();
680
+ const sections = [];
681
+ for (const el of root.querySelectorAll("[data-ohw-section]")) {
682
+ const id = el.getAttribute("data-ohw-section") ?? "";
683
+ if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
684
+ if (el.parentElement?.closest("[data-ohw-section]")) continue;
685
+ if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
686
+ continue;
687
+ seen.add(id);
688
+ const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
689
+ sections.push({ id, label });
690
+ }
691
+ return sections;
692
+ }
693
+ function collectSectionsFromDom() {
694
+ if (typeof document === "undefined") return [];
695
+ return parseSectionsFromRoot(document);
696
+ }
697
+ function parseSectionsFromHtml(html) {
698
+ const doc = new DOMParser().parseFromString(html, "text/html");
699
+ return parseSectionsFromRoot(doc);
700
+ }
701
+
702
+ // src/lib/section-instances.ts
703
+ var SECTION_ORDER_KEY = "__ohw_section_order";
704
+ var REMOVED_ATTR = "data-ohw-section-removed";
705
+ function isRemovedSection(el) {
706
+ return el.hasAttribute(REMOVED_ATTR);
707
+ }
708
+ function topLevelSections() {
709
+ return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
710
+ (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isRemovedSection(el)
711
+ );
712
+ }
713
+ function instanceIdOf(el) {
714
+ return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
715
+ }
716
+ function findByInstanceId(instanceId) {
717
+ const escapedId = CSS.escape(instanceId);
718
+ return document.querySelector(`[data-ohw-instance="${escapedId}"]`) ?? document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
719
+ }
720
+ function planSectionMove(instanceId, targetIndex, currentPath) {
721
+ const sections = topLevelSections();
722
+ const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
723
+ if (index === -1) return null;
724
+ const dragged = sections[index];
725
+ const others = sections.filter((_, i) => i !== index);
726
+ const clamped = Math.max(0, Math.min(targetIndex, others.length));
727
+ const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
728
+ return reordered.map((el, order) => ({
729
+ instanceId: instanceIdOf(el),
730
+ type: el.getAttribute("data-ohw-section") ?? "",
731
+ order,
732
+ pagePath: currentPath
733
+ }));
734
+ }
735
+ function moveSectionInstance(instanceId, direction, currentPath) {
736
+ const sections = topLevelSections();
737
+ const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
738
+ if (index === -1) return null;
739
+ const siblingIndex = direction === "up" ? index - 1 : index + 1;
740
+ if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
741
+ const entries = planSectionMove(instanceId, siblingIndex, currentPath);
742
+ if (!entries) return null;
743
+ applyPersistedOrder(entries);
744
+ return entries;
745
+ }
746
+ function syncRemovedFlags(entries) {
747
+ const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
748
+ document.querySelectorAll(`[${REMOVED_ATTR}]`).forEach((el) => {
749
+ if (!removedIds.has(instanceIdOf(el))) {
750
+ el.style.removeProperty("display");
751
+ el.removeAttribute(REMOVED_ATTR);
752
+ }
753
+ });
754
+ for (const id of removedIds) {
755
+ const el = findByInstanceId(id);
756
+ if (el) {
757
+ el.style.display = "none";
758
+ el.setAttribute(REMOVED_ATTR, "");
759
+ }
760
+ }
761
+ }
762
+ function applyPersistedOrder(entries) {
763
+ syncRemovedFlags(entries);
764
+ if (entries.length === 0) return;
765
+ const sections = topLevelSections();
766
+ if (sections.length === 0) return;
767
+ const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
768
+ const ordered = [...sections].sort((a, b) => {
769
+ const aOrder = orderIndex.get(instanceIdOf(a));
770
+ const bOrder = orderIndex.get(instanceIdOf(b));
771
+ if (aOrder === void 0 && bOrder === void 0) return 0;
772
+ if (aOrder === void 0) return 1;
773
+ if (bOrder === void 0) return -1;
774
+ return aOrder - bOrder;
775
+ });
776
+ let prev = null;
777
+ for (const el of ordered) {
778
+ if (prev) prev.after(el);
779
+ prev = el;
780
+ }
781
+ }
782
+ function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
783
+ if (!findByInstanceId(instanceId)) return null;
784
+ const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
785
+ const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
786
+ (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
787
+ );
788
+ allSections.forEach((el, order) => {
789
+ const id = instanceIdOf(el);
790
+ if (!byId.has(id)) {
791
+ byId.set(id, { instanceId: id, type: el.getAttribute("data-ohw-section") ?? "", order, pagePath: currentPath });
792
+ }
793
+ });
794
+ const target = byId.get(instanceId);
795
+ if (!target) return null;
796
+ byId.set(instanceId, { ...target, removed });
797
+ const entries = Array.from(byId.values());
798
+ applyPersistedOrder(entries);
799
+ return entries;
800
+ }
801
+ function deleteSectionInstance(instanceId, currentPath, existingEntries) {
802
+ return setSectionRemoved(instanceId, currentPath, existingEntries, true);
803
+ }
804
+ function restoreSectionInstance(instanceId, currentPath, existingEntries) {
805
+ return setSectionRemoved(instanceId, currentPath, existingEntries, false);
806
+ }
807
+ function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
808
+ const original = findByInstanceId(instanceId);
809
+ if (!original) return null;
810
+ const clone = original.cloneNode(true);
811
+ clone.setAttribute("data-ohw-instance", newId);
812
+ const keyRekeys = rekeySectionSubtree(clone, newId);
813
+ original.insertAdjacentElement("afterend", clone);
814
+ const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
815
+ const entries = topLevelSections().map((el, order) => {
816
+ const id = instanceIdOf(el);
817
+ return {
818
+ instanceId: id,
819
+ type: el.getAttribute("data-ohw-section") ?? "",
820
+ order,
821
+ pagePath: currentPath,
822
+ ...byId.get(id)?.removed ? { removed: true } : {}
823
+ };
824
+ });
825
+ applyPersistedOrder(entries);
826
+ return { entries, keyRekeys };
827
+ }
828
+ function newInstanceId() {
829
+ return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
830
+ }
831
+ function getPageSectionOrderEntries(raw, currentPath) {
832
+ if (!raw) return [];
833
+ try {
834
+ const entries = JSON.parse(raw);
835
+ return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
836
+ } catch {
837
+ return [];
838
+ }
839
+ }
840
+ function rekeySectionSubtree(root, instanceId) {
841
+ const suffix = `::${instanceId}`;
842
+ const pairs = [];
843
+ const rekey = (el, attr) => {
844
+ const current = el.getAttribute(attr);
845
+ if (!current) return;
846
+ const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
847
+ const next = `${base}${suffix}`;
848
+ el.setAttribute(attr, next);
849
+ pairs.push({ from: current, to: next });
850
+ };
851
+ if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
852
+ if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
853
+ root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
854
+ root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
855
+ return pairs;
856
+ }
857
+ function initSectionInstancesFromContent(content, currentPath) {
858
+ document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
859
+ el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
860
+ });
861
+ const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
862
+ for (const entry of entries) {
863
+ if (entry.instanceId === entry.type) continue;
864
+ if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
865
+ const original = document.querySelector(
866
+ `[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
867
+ );
868
+ if (!original) continue;
869
+ const clone = original.cloneNode(true);
870
+ clone.setAttribute("data-ohw-instance", entry.instanceId);
871
+ rekeySectionSubtree(clone, entry.instanceId);
872
+ original.insertAdjacentElement("afterend", clone);
873
+ }
874
+ applyPersistedOrder(entries);
875
+ }
876
+
640
877
  // src/ui/ai-tree/AiTreeRenderer.tsx
641
878
  var import_react = __toESM(require("react"), 1);
642
879
  var import_lucide_react = require("lucide-react");
@@ -2340,7 +2577,7 @@ function AiTreeRenderer({
2340
2577
  var import_jsx_runtime2 = require("react/jsx-runtime");
2341
2578
  var CONTAINER_ATTR = "data-ohw-ai-generated";
2342
2579
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
2343
- var REMOVED_ATTR = "data-ohw-ai-removed";
2580
+ var REMOVED_ATTR2 = "data-ohw-ai-removed";
2344
2581
  var TEMPLATE_HIDDEN_ATTR = "data-ohw-ai-template-hidden";
2345
2582
  var CHROME_SECTION_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
2346
2583
  function readRootVar(name) {
@@ -2434,18 +2671,18 @@ function placeContainer(container, entry) {
2434
2671
  }
2435
2672
  function syncRemovedSections(state) {
2436
2673
  const removed = new Set(state.removed ?? []);
2437
- for (const el of document.querySelectorAll(`[${REMOVED_ATTR}]`)) {
2674
+ for (const el of document.querySelectorAll(`[${REMOVED_ATTR2}]`)) {
2438
2675
  const id = el.getAttribute("data-ohw-section") ?? "";
2439
2676
  if (!removed.has(id)) {
2440
2677
  el.style.removeProperty("display");
2441
- el.removeAttribute(REMOVED_ATTR);
2678
+ el.removeAttribute(REMOVED_ATTR2);
2442
2679
  }
2443
2680
  }
2444
2681
  for (const id of removed) {
2445
2682
  const section = findTemplateSection(id);
2446
2683
  if (section && !section.hasAttribute(REPLACED_ATTR)) {
2447
2684
  section.style.display = "none";
2448
- section.setAttribute(REMOVED_ATTR, "");
2685
+ section.setAttribute(REMOVED_ATTR2, "");
2449
2686
  }
2450
2687
  }
2451
2688
  }
@@ -2462,7 +2699,7 @@ function syncTemplateHidden(state, pageHasSections) {
2462
2699
  if (el.hasAttribute(CONTAINER_ATTR)) continue;
2463
2700
  if (CHROME_SECTION_IDS.has(el.getAttribute("data-ohw-section") ?? "")) continue;
2464
2701
  if (el.parentElement?.closest("[data-ohw-section]")) continue;
2465
- if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR)) continue;
2702
+ if (el.hasAttribute(REPLACED_ATTR) || el.hasAttribute(REMOVED_ATTR2)) continue;
2466
2703
  el.style.display = "none";
2467
2704
  el.setAttribute(TEMPLATE_HIDDEN_ATTR, "");
2468
2705
  }
@@ -2486,18 +2723,23 @@ function syncReplacedOriginals(state) {
2486
2723
  }
2487
2724
  }
2488
2725
  var sectionOrderIndex = /* @__PURE__ */ new Map();
2726
+ var removedSectionIds = /* @__PURE__ */ new Set();
2489
2727
  function setAiSectionOrder(raw, currentPath) {
2490
2728
  const next = /* @__PURE__ */ new Map();
2729
+ const removed = /* @__PURE__ */ new Set();
2491
2730
  if (raw) {
2492
2731
  try {
2493
2732
  const entries = JSON.parse(raw);
2494
2733
  for (const entry of entries) {
2495
- if (!entry.pagePath || entry.pagePath === currentPath) next.set(entry.instanceId, entry.order);
2734
+ if (entry.pagePath && entry.pagePath !== currentPath) continue;
2735
+ next.set(entry.instanceId, entry.order);
2736
+ if (entry.removed) removed.add(entry.instanceId);
2496
2737
  }
2497
2738
  } catch {
2498
2739
  }
2499
2740
  }
2500
2741
  sectionOrderIndex = next;
2742
+ removedSectionIds = removed;
2501
2743
  }
2502
2744
  function applyExplicitOrder(entries) {
2503
2745
  if (sectionOrderIndex.size === 0) return entries;
@@ -2533,6 +2775,18 @@ function orderByChain(sections) {
2533
2775
  for (const root of roots) visit(root);
2534
2776
  return out.length === sections.length ? out : sections;
2535
2777
  }
2778
+ function syncSoftRemovedGenerated() {
2779
+ for (const [id, section] of mounted) {
2780
+ const el = section.container;
2781
+ if (removedSectionIds.has(id)) {
2782
+ el.style.display = "none";
2783
+ el.setAttribute(REMOVED_ATTR, "");
2784
+ } else if (el.hasAttribute(REMOVED_ATTR)) {
2785
+ el.style.removeProperty("display");
2786
+ el.removeAttribute(REMOVED_ATTR);
2787
+ }
2788
+ }
2789
+ }
2536
2790
  function applyAiSectionsToDom(state, options) {
2537
2791
  if (typeof document === "undefined") return;
2538
2792
  const brandOverride = deriveBrandOverride();
@@ -2600,6 +2854,7 @@ function applyAiSectionsToDom(state, options) {
2600
2854
  syncReplacedOriginals(state);
2601
2855
  syncRemovedSections(state);
2602
2856
  syncTemplateHidden(state, pageSections.length > 0);
2857
+ syncSoftRemovedGenerated();
2603
2858
  }
2604
2859
 
2605
2860
  // src/useLinkHrefGuardian.ts
@@ -8560,215 +8815,6 @@ function CarouselOverlay({
8560
8815
  // src/ui/ai-section/AiSectionOverlay.tsx
8561
8816
  var import_react8 = require("react");
8562
8817
  var import_lucide_react7 = require("lucide-react");
8563
-
8564
- // src/lib/sections.ts
8565
- var LINK_PICKER_EXCLUDED_IDS = /* @__PURE__ */ new Set(["navbar", "footer"]);
8566
- function isChromeSection(el) {
8567
- return el.matches("header, nav, footer, aside");
8568
- }
8569
- function titleCaseSectionId(id) {
8570
- return id.split("-").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
8571
- }
8572
- function parseSectionsFromRoot(root) {
8573
- const seen = /* @__PURE__ */ new Set();
8574
- const sections = [];
8575
- for (const el of root.querySelectorAll("[data-ohw-section]")) {
8576
- const id = el.getAttribute("data-ohw-section") ?? "";
8577
- if (!id || seen.has(id) || LINK_PICKER_EXCLUDED_IDS.has(id)) continue;
8578
- if (el.parentElement?.closest("[data-ohw-section]")) continue;
8579
- if (el.hasAttribute("data-ohw-ai-template-hidden") || el.hasAttribute("data-ohw-ai-removed") || el.hasAttribute("data-ohw-ai-replaced-by"))
8580
- continue;
8581
- seen.add(id);
8582
- const label = el.getAttribute("data-ohw-section-label") ?? titleCaseSectionId(id);
8583
- sections.push({ id, label });
8584
- }
8585
- return sections;
8586
- }
8587
- function collectSectionsFromDom() {
8588
- if (typeof document === "undefined") return [];
8589
- return parseSectionsFromRoot(document);
8590
- }
8591
- function parseSectionsFromHtml(html) {
8592
- const doc = new DOMParser().parseFromString(html, "text/html");
8593
- return parseSectionsFromRoot(doc);
8594
- }
8595
-
8596
- // src/lib/section-instances.ts
8597
- var SECTION_ORDER_KEY = "__ohw_section_order";
8598
- var REMOVED_ATTR2 = "data-ohw-section-removed";
8599
- function isRemovedSection(el) {
8600
- return el.hasAttribute(REMOVED_ATTR2);
8601
- }
8602
- function topLevelSections() {
8603
- return Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8604
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el) && !isRemovedSection(el)
8605
- );
8606
- }
8607
- function instanceIdOf(el) {
8608
- return el.dataset.ohwInstance ?? el.dataset.ohwSection ?? "";
8609
- }
8610
- function findByInstanceId(instanceId) {
8611
- const escapedId = CSS.escape(instanceId);
8612
- return document.querySelector(`[data-ohw-instance="${escapedId}"]`) ?? document.querySelector(`[data-ohw-section="${escapedId}"]:not([data-ohw-instance])`);
8613
- }
8614
- function planSectionMove(instanceId, targetIndex, currentPath) {
8615
- const sections = topLevelSections();
8616
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8617
- if (index === -1) return null;
8618
- const dragged = sections[index];
8619
- const others = sections.filter((_, i) => i !== index);
8620
- const clamped = Math.max(0, Math.min(targetIndex, others.length));
8621
- const reordered = [...others.slice(0, clamped), dragged, ...others.slice(clamped)];
8622
- return reordered.map((el, order) => ({
8623
- instanceId: instanceIdOf(el),
8624
- type: el.getAttribute("data-ohw-section") ?? "",
8625
- order,
8626
- pagePath: currentPath
8627
- }));
8628
- }
8629
- function moveSectionInstance(instanceId, direction, currentPath) {
8630
- const sections = topLevelSections();
8631
- const index = sections.findIndex((el) => instanceIdOf(el) === instanceId);
8632
- if (index === -1) return null;
8633
- const siblingIndex = direction === "up" ? index - 1 : index + 1;
8634
- if (siblingIndex < 0 || siblingIndex >= sections.length) return null;
8635
- const entries = planSectionMove(instanceId, siblingIndex, currentPath);
8636
- if (!entries) return null;
8637
- applyPersistedOrder(entries);
8638
- return entries;
8639
- }
8640
- function syncRemovedFlags(entries) {
8641
- const removedIds = new Set(entries.filter((e) => e.removed).map((e) => e.instanceId));
8642
- document.querySelectorAll(`[${REMOVED_ATTR2}]`).forEach((el) => {
8643
- if (!removedIds.has(instanceIdOf(el))) {
8644
- el.style.removeProperty("display");
8645
- el.removeAttribute(REMOVED_ATTR2);
8646
- }
8647
- });
8648
- for (const id of removedIds) {
8649
- const el = findByInstanceId(id);
8650
- if (el) {
8651
- el.style.display = "none";
8652
- el.setAttribute(REMOVED_ATTR2, "");
8653
- }
8654
- }
8655
- }
8656
- function applyPersistedOrder(entries) {
8657
- syncRemovedFlags(entries);
8658
- if (entries.length === 0) return;
8659
- const sections = topLevelSections();
8660
- if (sections.length === 0) return;
8661
- const orderIndex = new Map(entries.map((e) => [e.instanceId, e.order]));
8662
- const ordered = [...sections].sort((a, b) => {
8663
- const aOrder = orderIndex.get(instanceIdOf(a));
8664
- const bOrder = orderIndex.get(instanceIdOf(b));
8665
- if (aOrder === void 0 && bOrder === void 0) return 0;
8666
- if (aOrder === void 0) return 1;
8667
- if (bOrder === void 0) return -1;
8668
- return aOrder - bOrder;
8669
- });
8670
- let prev = null;
8671
- for (const el of ordered) {
8672
- if (prev) prev.after(el);
8673
- prev = el;
8674
- }
8675
- }
8676
- function setSectionRemoved(instanceId, currentPath, existingEntries, removed) {
8677
- if (!findByInstanceId(instanceId)) return null;
8678
- const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
8679
- const allSections = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
8680
- (el) => !el.parentElement?.closest("[data-ohw-section]") && !isChromeSection(el)
8681
- );
8682
- allSections.forEach((el, order) => {
8683
- const id = instanceIdOf(el);
8684
- if (!byId.has(id)) {
8685
- byId.set(id, { instanceId: id, type: el.getAttribute("data-ohw-section") ?? "", order, pagePath: currentPath });
8686
- }
8687
- });
8688
- const target = byId.get(instanceId);
8689
- if (!target) return null;
8690
- byId.set(instanceId, { ...target, removed });
8691
- const entries = Array.from(byId.values());
8692
- applyPersistedOrder(entries);
8693
- return entries;
8694
- }
8695
- function deleteSectionInstance(instanceId, currentPath, existingEntries) {
8696
- return setSectionRemoved(instanceId, currentPath, existingEntries, true);
8697
- }
8698
- function restoreSectionInstance(instanceId, currentPath, existingEntries) {
8699
- return setSectionRemoved(instanceId, currentPath, existingEntries, false);
8700
- }
8701
- function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
8702
- const original = findByInstanceId(instanceId);
8703
- if (!original) return null;
8704
- const clone = original.cloneNode(true);
8705
- clone.setAttribute("data-ohw-instance", newId);
8706
- const keyRekeys = rekeySectionSubtree(clone, newId);
8707
- original.insertAdjacentElement("afterend", clone);
8708
- const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
8709
- const entries = topLevelSections().map((el, order) => {
8710
- const id = instanceIdOf(el);
8711
- return {
8712
- instanceId: id,
8713
- type: el.getAttribute("data-ohw-section") ?? "",
8714
- order,
8715
- pagePath: currentPath,
8716
- ...byId.get(id)?.removed ? { removed: true } : {}
8717
- };
8718
- });
8719
- applyPersistedOrder(entries);
8720
- return { entries, keyRekeys };
8721
- }
8722
- function newInstanceId() {
8723
- return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
8724
- }
8725
- function getPageSectionOrderEntries(raw, currentPath) {
8726
- if (!raw) return [];
8727
- try {
8728
- const entries = JSON.parse(raw);
8729
- return entries.filter((e) => !e.pagePath || e.pagePath === currentPath);
8730
- } catch {
8731
- return [];
8732
- }
8733
- }
8734
- function rekeySectionSubtree(root, instanceId) {
8735
- const suffix = `::${instanceId}`;
8736
- const pairs = [];
8737
- const rekey = (el, attr) => {
8738
- const current = el.getAttribute(attr);
8739
- if (!current) return;
8740
- const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
8741
- const next = `${base}${suffix}`;
8742
- el.setAttribute(attr, next);
8743
- pairs.push({ from: current, to: next });
8744
- };
8745
- if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
8746
- if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
8747
- root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
8748
- root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
8749
- return pairs;
8750
- }
8751
- function initSectionInstancesFromContent(content, currentPath) {
8752
- document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
8753
- el.setAttribute("data-ohw-instance", el.getAttribute("data-ohw-section") ?? "");
8754
- });
8755
- const entries = getPageSectionOrderEntries(content[SECTION_ORDER_KEY], currentPath);
8756
- for (const entry of entries) {
8757
- if (entry.instanceId === entry.type) continue;
8758
- if (document.querySelector(`[data-ohw-instance="${CSS.escape(entry.instanceId)}"]`)) continue;
8759
- const original = document.querySelector(
8760
- `[data-ohw-section="${CSS.escape(entry.type)}"][data-ohw-instance="${CSS.escape(entry.type)}"]`
8761
- );
8762
- if (!original) continue;
8763
- const clone = original.cloneNode(true);
8764
- clone.setAttribute("data-ohw-instance", entry.instanceId);
8765
- rekeySectionSubtree(clone, entry.instanceId);
8766
- original.insertAdjacentElement("afterend", clone);
8767
- }
8768
- applyPersistedOrder(entries);
8769
- }
8770
-
8771
- // src/ui/ai-section/AiSectionOverlay.tsx
8772
8818
  var import_jsx_runtime17 = require("react/jsx-runtime");
8773
8819
  function findSectionElement(instanceId) {
8774
8820
  const escaped = CSS.escape(instanceId);
@@ -20555,6 +20601,44 @@ function OhhwellsBridge() {
20555
20601
  if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20556
20602
  }, 400));
20557
20603
  };
20604
+ const reapCommittedAiSections = (excludeIds) => {
20605
+ const aiState = parseAiSectionsState(aiSectionsRef.current);
20606
+ if (aiState.sections.length === 0) return [];
20607
+ let orderEntries = [];
20608
+ try {
20609
+ const parsed = JSON.parse(editContentRef.current[SECTION_ORDER_KEY] ?? "[]");
20610
+ if (Array.isArray(parsed)) orderEntries = parsed;
20611
+ } catch {
20612
+ return [];
20613
+ }
20614
+ const removedIds = orderEntries.filter((entry) => entry && typeof entry.instanceId === "string" && entry.removed).map((entry) => entry.instanceId);
20615
+ if (removedIds.length === 0) return [];
20616
+ const result = reapRemovedAiSections(aiState, parseStyleStore(stylesRef.current), removedIds, excludeIds);
20617
+ if (!result.changed) return [];
20618
+ const nodes = [];
20619
+ aiSectionsRef.current = serializeAiSectionsState(result.state);
20620
+ nodes.push({ key: AI_SECTIONS_KEY, text: aiSectionsRef.current });
20621
+ const reaped = new Set(result.reapedIds);
20622
+ const nextOrderJson = JSON.stringify(orderEntries.filter((entry) => !reaped.has(entry.instanceId)));
20623
+ editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: nextOrderJson };
20624
+ setAiSectionOrder(nextOrderJson, window.location.pathname);
20625
+ nodes.push({ key: SECTION_ORDER_KEY, text: nextOrderJson });
20626
+ if (result.store) {
20627
+ stylesRef.current = JSON.stringify(result.store);
20628
+ nodes.push({ key: STYLE_STORE_KEY, text: stylesRef.current });
20629
+ }
20630
+ const nextContent = { ...editContentRef.current };
20631
+ for (const key of Object.keys(nextContent)) {
20632
+ if (key.startsWith(AI_SLOT_KEY_PREFIX) && result.slotPrefixes.some((prefix) => key.startsWith(prefix)) && nextContent[key] !== "") {
20633
+ nextContent[key] = "";
20634
+ nodes.push({ key, text: "" });
20635
+ }
20636
+ }
20637
+ editContentRef.current = nextContent;
20638
+ applyAiSectionsToDom(result.state);
20639
+ applyStylesToDom(parseStyleStore(stylesRef.current));
20640
+ return nodes;
20641
+ };
20558
20642
  const handleHydrate = (e) => {
20559
20643
  if (e.data?.type !== "ow:hydrate") return;
20560
20644
  const content = e.data.content;
@@ -20626,6 +20710,11 @@ function OhhwellsBridge() {
20626
20710
  reconcileFooterOrderFromContent(editContentRef.current);
20627
20711
  syncNavigationDragCursorAttrs();
20628
20712
  enforceLinkHrefs();
20713
+ const hydrateReapExclude = /* @__PURE__ */ new Set();
20714
+ const hydratePendingUndo = pendingDeleteUndoRef.current;
20715
+ if (hydratePendingUndo?.sectionInstanceId) hydrateReapExclude.add(hydratePendingUndo.sectionInstanceId);
20716
+ const reapNodes = reapCommittedAiSections(hydrateReapExclude);
20717
+ if (reapNodes.length > 0) postToParentRef.current({ type: "ow:change", nodes: reapNodes });
20629
20718
  const hydratedHeight = document.body.scrollHeight;
20630
20719
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
20631
20720
  postToParentRef.current({ type: "ow:hydrate-done" });
@@ -20832,6 +20921,7 @@ function OhhwellsBridge() {
20832
20921
  if (!entries) return;
20833
20922
  const orderJson = JSON.stringify(entries);
20834
20923
  editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20924
+ setAiSectionOrder(orderJson, window.location.pathname);
20835
20925
  postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
20836
20926
  aiSectionApiRef.current?.clear();
20837
20927
  window.dispatchEvent(new Event("resize"));
@@ -20840,6 +20930,7 @@ function OhhwellsBridge() {
20840
20930
  const actionId = newInstanceId();
20841
20931
  pendingDeleteUndoRef.current = {
20842
20932
  actionId,
20933
+ sectionInstanceId: instanceId,
20843
20934
  restore: () => {
20844
20935
  const restoredEntries = getPageSectionOrderEntries(
20845
20936
  editContentRef.current[SECTION_ORDER_KEY],
@@ -20849,6 +20940,7 @@ function OhhwellsBridge() {
20849
20940
  if (!restored) return;
20850
20941
  const restoredJson = JSON.stringify(restored);
20851
20942
  editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: restoredJson };
20943
+ setAiSectionOrder(restoredJson, window.location.pathname);
20852
20944
  postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: restoredJson }] });
20853
20945
  window.dispatchEvent(new Event("resize"));
20854
20946
  const restoreHeight = document.body.scrollHeight;
@@ -21153,6 +21245,10 @@ function OhhwellsBridge() {
21153
21245
  };
21154
21246
  const handleSave = (e) => {
21155
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);
21156
21252
  const nodes = collectEditableNodes(editContentRef.current);
21157
21253
  const tracker = document.querySelector("[data-ohw-sections-tracker]");
21158
21254
  if (tracker?.textContent) nodes.push({ key: "__ohw_sections", type: "sections", text: tracker.textContent });
@@ -21172,6 +21268,11 @@ function OhhwellsBridge() {
21172
21268
  const text = specs.length ? JSON.stringify(specs) : editContentRef.current[fieldsKey(formKey)];
21173
21269
  if (text) nodes.push({ key: fieldsKey(formKey), type: "text", text });
21174
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
+ }
21175
21276
  postToParentRef.current({ type: "ow:save-result", nodes });
21176
21277
  };
21177
21278
  const handleInsertSection = (e) => {
@@ -21797,7 +21898,7 @@ function OhhwellsBridge() {
21797
21898
  postToParent2({
21798
21899
  type: "ow:ready",
21799
21900
  version: "1",
21800
- bridgeVersion: "0.1.85",
21901
+ bridgeVersion: "0.1.86",
21801
21902
  path: pathname,
21802
21903
  nodes: collectEditableNodes(editContentRef.current),
21803
21904
  sections