@ohhwells/bridge 0.1.124 → 0.1.125

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
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  DropdownMenuItem: () => DropdownMenuItem,
47
47
  DropdownMenuSeparator: () => DropdownMenuSeparator,
48
48
  DropdownMenuTrigger: () => DropdownMenuTrigger,
49
+ EmptySection: () => EmptySection,
49
50
  ItemActionToolbar: () => ItemActionToolbar,
50
51
  ItemInteractionLayer: () => ItemInteractionLayer,
51
52
  LinkEditorPanel: () => LinkEditorPanel,
@@ -19618,21 +19619,10 @@ function parseSchedulingInsertAfter(insertAfter) {
19618
19619
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
19619
19620
  };
19620
19621
  }
19621
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
19622
- const parsed = parseSchedulingInsertAfter(insertAfter);
19623
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
19624
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
19625
- return { effectiveInsertAfter, insertBefore };
19626
- }
19627
- function getSchedulingMountPoint(insertAfter) {
19628
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
19629
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
19630
- if (!anchorEl && anchor === "scheduling") {
19631
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
19632
- anchorEl = widgets.at(-1) ?? null;
19633
- }
19634
- if (!anchorEl) return null;
19635
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
19622
+ function resolveEntryAnchor(entry) {
19623
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
19624
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
19625
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
19636
19626
  }
19637
19627
  function schedulingMountDepth(insertAfter) {
19638
19628
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -19649,8 +19639,7 @@ function getPageSchedulingEntries(raw) {
19649
19639
  }
19650
19640
  }
19651
19641
  function isSchedulingWidgetMissing(entry) {
19652
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
19653
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
19642
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
19654
19643
  }
19655
19644
  function hasMissingSchedulingWidgets(entries) {
19656
19645
  return entries.some(isSchedulingWidgetMissing);
@@ -19688,17 +19677,18 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
19688
19677
  } catch {
19689
19678
  }
19690
19679
  }
19691
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
19692
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
19693
- const sectionId = schedulingSectionId(effectiveInsertAfter);
19680
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
19681
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
19682
+ const sectionId = schedulingSectionId(widgetId);
19694
19683
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
19695
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
19696
- if (!mountPoint) return false;
19684
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
19685
+ if (!anchorEl) return false;
19686
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
19697
19687
  const container = document.createElement("div");
19698
19688
  container.dataset.ohwSectionContainer = "scheduling";
19699
19689
  container.dataset.ohwInstance = sectionId;
19700
- if (insertBefore) {
19701
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
19690
+ if (beforeId) {
19691
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
19702
19692
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
19703
19693
  if (!beforePoint) return false;
19704
19694
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -19709,20 +19699,26 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
19709
19699
  }
19710
19700
  tail.insertAdjacentElement("afterend", container);
19711
19701
  }
19712
- const root = (0, import_client2.createRoot)(container);
19713
- schedulingRoots.set(container, root);
19714
- (0, import_react_dom4.flushSync)(() => {
19715
- root.render(
19716
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
19717
- SchedulingWidget,
19718
- {
19719
- notifyOnConnect,
19720
- initialScheduleId: scheduleId,
19721
- insertAfter: effectiveInsertAfter
19722
- }
19723
- )
19724
- );
19725
- });
19702
+ try {
19703
+ const root = (0, import_client2.createRoot)(container);
19704
+ schedulingRoots.set(container, root);
19705
+ (0, import_react_dom4.flushSync)(() => {
19706
+ root.render(
19707
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
19708
+ SchedulingWidget,
19709
+ {
19710
+ notifyOnConnect,
19711
+ initialScheduleId: scheduleId,
19712
+ insertAfter: widgetId
19713
+ }
19714
+ )
19715
+ );
19716
+ });
19717
+ } catch (err) {
19718
+ console.error("[ow:scheduling] render threw", err);
19719
+ container.remove();
19720
+ return false;
19721
+ }
19726
19722
  const tracker = getSectionsTracker();
19727
19723
  let sections = [];
19728
19724
  try {
@@ -19730,10 +19726,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
19730
19726
  } catch {
19731
19727
  }
19732
19728
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
19733
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
19729
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
19734
19730
  sections.push({
19735
19731
  type: "scheduling",
19736
- insertAfter: effectiveInsertAfter,
19732
+ insertAfter: widgetId,
19733
+ anchorId,
19734
+ beforeId: beforeId ?? null,
19737
19735
  pagePath: window.location.pathname,
19738
19736
  ...scheduleId ? { scheduleId } : {}
19739
19737
  });
@@ -19747,7 +19745,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
19747
19745
  for (let i = pending.length - 1; i >= 0; i--) {
19748
19746
  const entry = pending[i];
19749
19747
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
19750
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
19748
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
19749
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
19751
19750
  pending.splice(i, 1);
19752
19751
  }
19753
19752
  }
@@ -20810,6 +20809,7 @@ function StateToggle({
20810
20809
  );
20811
20810
  }
20812
20811
  var contentCache = /* @__PURE__ */ new Map();
20812
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
20813
20813
  var brandingCache = /* @__PURE__ */ new Map();
20814
20814
  var OHW_LOADER_STYLE = {
20815
20815
  position: "fixed",
@@ -21016,6 +21016,7 @@ function OhhwellsBridge() {
21016
21016
  if (!subdomainFromQuery || typeof window === "undefined") return;
21017
21017
  const url = new URL(window.location.href);
21018
21018
  if (!url.searchParams.has("subdomain")) return;
21019
+ if (!resolveSubdomain(null)) return;
21019
21020
  url.searchParams.delete("subdomain");
21020
21021
  window.history.replaceState(null, "", url.pathname + url.search + url.hash);
21021
21022
  }, [subdomainFromQuery]);
@@ -23062,7 +23063,9 @@ function OhhwellsBridge() {
23062
23063
  let cancelled = false;
23063
23064
  setFetchState("loading");
23064
23065
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
23065
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
23066
+ const initialPath = pathname;
23067
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
23068
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
23066
23069
  if (cancelled) return;
23067
23070
  const content = data?.content ?? {};
23068
23071
  const branding = Boolean(data?.showBranding);
@@ -23259,6 +23262,17 @@ function OhhwellsBridge() {
23259
23262
  debounceTimer = setTimeout(applyFromCache, 150);
23260
23263
  };
23261
23264
  applyFromCache();
23265
+ const pathCacheKey = `${subdomain}::${pathname}`;
23266
+ if (!fetchedContentPaths.has(pathCacheKey)) {
23267
+ fetchedContentPaths.add(pathCacheKey);
23268
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
23269
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
23270
+ if (!data?.content) return;
23271
+ contentCache.set(subdomain, data.content);
23272
+ applyFromCache();
23273
+ }).catch(() => {
23274
+ });
23275
+ }
23262
23276
  observer = new MutationObserver(scheduleApply);
23263
23277
  observer.observe(document.body, { childList: true, subtree: true });
23264
23278
  return () => {
@@ -26145,8 +26159,12 @@ function OhhwellsBridge() {
26145
26159
  if (inserted) {
26146
26160
  const tracker = getSectionsTracker();
26147
26161
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
26148
- const h = document.body.scrollHeight;
26149
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
26162
+ const reportHeight = () => {
26163
+ const h = document.body.scrollHeight;
26164
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
26165
+ };
26166
+ reportHeight();
26167
+ setTimeout(reportHeight, 500);
26150
26168
  }
26151
26169
  };
26152
26170
  const handleSwitchSchedule = (e) => {
@@ -26872,7 +26890,7 @@ function OhhwellsBridge() {
26872
26890
  postToParent2({
26873
26891
  type: "ow:ready",
26874
26892
  version: "1",
26875
- bridgeVersion: "0.1.123",
26893
+ bridgeVersion: "0.1.124",
26876
26894
  path: pathname,
26877
26895
  nodes: collectEditableNodes(editContentRef.current),
26878
26896
  sections
@@ -27959,6 +27977,59 @@ function OhhwellsBridge() {
27959
27977
  ) : null
27960
27978
  ] });
27961
27979
  }
27980
+
27981
+ // src/ui/EmptySection.tsx
27982
+ var import_link = __toESM(require("next/link"), 1);
27983
+ var import_jsx_runtime37 = require("react/jsx-runtime");
27984
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
27985
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
27986
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
27987
+ "p",
27988
+ {
27989
+ style: {
27990
+ fontFamily: "var(--brand-font-body)",
27991
+ fontSize: "0.75rem",
27992
+ fontWeight: 500,
27993
+ letterSpacing: "0.15em",
27994
+ textTransform: "uppercase",
27995
+ color: "var(--brand-accent)",
27996
+ marginBottom: "1.5rem"
27997
+ },
27998
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
27999
+ }
28000
+ ),
28001
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
28002
+ "h1",
28003
+ {
28004
+ style: {
28005
+ fontFamily: "var(--brand-font-heading)",
28006
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
28007
+ lineHeight: 1.1,
28008
+ letterSpacing: "-0.025em",
28009
+ color: "var(--brand-text)",
28010
+ marginBottom: "1rem"
28011
+ },
28012
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
28013
+ children: title
28014
+ }
28015
+ ),
28016
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
28017
+ "p",
28018
+ {
28019
+ style: {
28020
+ fontFamily: "var(--brand-font-body)",
28021
+ fontSize: "1rem",
28022
+ lineHeight: 1.7,
28023
+ fontWeight: 300,
28024
+ color: "var(--brand-text-muted)",
28025
+ maxWidth: "340px"
28026
+ },
28027
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
28028
+ children: "This page doesn't have any content yet."
28029
+ }
28030
+ )
28031
+ ] });
28032
+ }
27962
28033
  // Annotate the CommonJS export names for ESM import in node:
27963
28034
  0 && (module.exports = {
27964
28035
  AI_DEFAULT_BRAND,
@@ -27976,6 +28047,7 @@ function OhhwellsBridge() {
27976
28047
  DropdownMenuItem,
27977
28048
  DropdownMenuSeparator,
27978
28049
  DropdownMenuTrigger,
28050
+ EmptySection,
27979
28051
  ItemActionToolbar,
27980
28052
  ItemInteractionLayer,
27981
28053
  LinkEditorPanel,