@ohhwells/bridge 0.1.54-next.147 → 0.1.54-next.149

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,
@@ -2026,7 +2027,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2026
2027
  const autoId = (0, import_react5.useId)();
2027
2028
  const insertAfter = insertAfterProp ?? autoId;
2028
2029
  const [schedule, setSchedule] = (0, import_react5.useState)(null);
2029
- const [loading, setLoading] = (0, import_react5.useState)(true);
2030
+ const [loading, setLoading] = (0, import_react5.useState)(initialScheduleId !== null);
2030
2031
  const [inEditor, setInEditor] = (0, import_react5.useState)(false);
2031
2032
  const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2032
2033
  const [modalState, setModalState] = (0, import_react5.useState)(null);
@@ -2200,8 +2201,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2200
2201
  "*"
2201
2202
  );
2202
2203
  };
2203
- if (!inEditor && !loading && !schedule) return null;
2204
2204
  const sectionId = `scheduling-${insertAfter}`;
2205
+ if (!inEditor && !loading && !schedule) {
2206
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2207
+ }
2205
2208
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2206
2209
  "section",
2207
2210
  {
@@ -12248,21 +12251,10 @@ function parseSchedulingInsertAfter(insertAfter) {
12248
12251
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
12249
12252
  };
12250
12253
  }
12251
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
12252
- const parsed = parseSchedulingInsertAfter(insertAfter);
12253
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
12254
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
12255
- return { effectiveInsertAfter, insertBefore };
12256
- }
12257
- function getSchedulingMountPoint(insertAfter) {
12258
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
12259
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
12260
- if (!anchorEl && anchor === "scheduling") {
12261
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
12262
- anchorEl = widgets.at(-1) ?? null;
12263
- }
12264
- if (!anchorEl) return null;
12265
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
12254
+ function resolveEntryAnchor(entry) {
12255
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
12256
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
12257
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
12266
12258
  }
12267
12259
  function schedulingMountDepth(insertAfter) {
12268
12260
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -12279,8 +12271,7 @@ function getPageSchedulingEntries(raw) {
12279
12271
  }
12280
12272
  }
12281
12273
  function isSchedulingWidgetMissing(entry) {
12282
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
12283
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
12274
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
12284
12275
  }
12285
12276
  function hasMissingSchedulingWidgets(entries) {
12286
12277
  return entries.some(isSchedulingWidgetMissing);
@@ -12310,16 +12301,17 @@ function initSectionsFromContent(content, removeExisting = false) {
12310
12301
  } catch {
12311
12302
  }
12312
12303
  }
12313
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
12314
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
12315
- const sectionId = schedulingSectionId(effectiveInsertAfter);
12304
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
12305
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
12306
+ const sectionId = schedulingSectionId(widgetId);
12316
12307
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
12317
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
12318
- if (!mountPoint) return false;
12308
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
12309
+ if (!anchorEl) return false;
12310
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
12319
12311
  const container = document.createElement("div");
12320
12312
  container.dataset.ohwSectionContainer = "scheduling";
12321
- if (insertBefore) {
12322
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
12313
+ if (beforeId) {
12314
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
12323
12315
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
12324
12316
  if (!beforePoint) return false;
12325
12317
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -12330,19 +12322,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12330
12322
  }
12331
12323
  tail.insertAdjacentElement("afterend", container);
12332
12324
  }
12333
- const root = (0, import_client2.createRoot)(container);
12334
- (0, import_react_dom3.flushSync)(() => {
12335
- root.render(
12336
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
12337
- SchedulingWidget,
12338
- {
12339
- notifyOnConnect,
12340
- initialScheduleId: scheduleId,
12341
- insertAfter: effectiveInsertAfter
12342
- }
12343
- )
12344
- );
12345
- });
12325
+ try {
12326
+ const root = (0, import_client2.createRoot)(container);
12327
+ (0, import_react_dom3.flushSync)(() => {
12328
+ root.render(
12329
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
12330
+ SchedulingWidget,
12331
+ {
12332
+ notifyOnConnect,
12333
+ initialScheduleId: scheduleId,
12334
+ insertAfter: widgetId
12335
+ }
12336
+ )
12337
+ );
12338
+ });
12339
+ } catch (err) {
12340
+ console.error("[ow:scheduling] render threw", err);
12341
+ container.remove();
12342
+ return false;
12343
+ }
12346
12344
  const tracker = getSectionsTracker();
12347
12345
  let sections = [];
12348
12346
  try {
@@ -12350,10 +12348,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12350
12348
  } catch {
12351
12349
  }
12352
12350
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
12353
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
12351
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
12354
12352
  sections.push({
12355
12353
  type: "scheduling",
12356
- insertAfter: effectiveInsertAfter,
12354
+ insertAfter: widgetId,
12355
+ anchorId,
12356
+ beforeId: beforeId ?? null,
12357
12357
  pagePath: window.location.pathname,
12358
12358
  ...scheduleId ? { scheduleId } : {}
12359
12359
  });
@@ -12367,7 +12367,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
12367
12367
  for (let i = pending.length - 1; i >= 0; i--) {
12368
12368
  const entry = pending[i];
12369
12369
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
12370
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
12370
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
12371
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
12371
12372
  pending.splice(i, 1);
12372
12373
  }
12373
12374
  }
@@ -13303,6 +13304,7 @@ function StateToggle({
13303
13304
  );
13304
13305
  }
13305
13306
  var contentCache = /* @__PURE__ */ new Map();
13307
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
13306
13308
  function resolveSubdomain(subdomainFromQuery) {
13307
13309
  if (subdomainFromQuery) return subdomainFromQuery;
13308
13310
  if (typeof window !== "undefined") {
@@ -14641,7 +14643,9 @@ function OhhwellsBridge() {
14641
14643
  let cancelled = false;
14642
14644
  setFetchState("loading");
14643
14645
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
14644
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
14646
+ const initialPath = pathname;
14647
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
14648
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
14645
14649
  if (cancelled) return;
14646
14650
  const content = data?.content ?? {};
14647
14651
  contentCache.set(subdomain, content);
@@ -14701,6 +14705,17 @@ function OhhwellsBridge() {
14701
14705
  debounceTimer = setTimeout(applyFromCache, 150);
14702
14706
  };
14703
14707
  applyFromCache();
14708
+ const pathCacheKey = `${subdomain}::${pathname}`;
14709
+ if (!fetchedContentPaths.has(pathCacheKey)) {
14710
+ fetchedContentPaths.add(pathCacheKey);
14711
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
14712
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
14713
+ if (!data?.content) return;
14714
+ contentCache.set(subdomain, data.content);
14715
+ applyFromCache();
14716
+ }).catch(() => {
14717
+ });
14718
+ }
14704
14719
  observer = new MutationObserver(scheduleApply);
14705
14720
  observer.observe(document.body, { childList: true, subtree: true });
14706
14721
  return () => {
@@ -16529,8 +16544,12 @@ function OhhwellsBridge() {
16529
16544
  if (inserted) {
16530
16545
  const tracker = getSectionsTracker();
16531
16546
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
16532
- const h = document.documentElement.scrollHeight;
16533
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
16547
+ const reportHeight = () => {
16548
+ const h = document.body.scrollHeight;
16549
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
16550
+ };
16551
+ reportHeight();
16552
+ setTimeout(reportHeight, 500);
16534
16553
  }
16535
16554
  };
16536
16555
  const handleSwitchSchedule = (e) => {
@@ -17759,6 +17778,59 @@ function OhhwellsBridge() {
17759
17778
  bridgeRoot
17760
17779
  ) : null;
17761
17780
  }
17781
+
17782
+ // src/ui/EmptySection.tsx
17783
+ var import_link = __toESM(require("next/link"), 1);
17784
+ var import_jsx_runtime33 = require("react/jsx-runtime");
17785
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
17786
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
17787
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17788
+ "p",
17789
+ {
17790
+ style: {
17791
+ fontFamily: "var(--brand-font-body)",
17792
+ fontSize: "0.75rem",
17793
+ fontWeight: 500,
17794
+ letterSpacing: "0.15em",
17795
+ textTransform: "uppercase",
17796
+ color: "var(--brand-accent)",
17797
+ marginBottom: "1.5rem"
17798
+ },
17799
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
17800
+ }
17801
+ ),
17802
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17803
+ "h1",
17804
+ {
17805
+ style: {
17806
+ fontFamily: "var(--brand-font-heading)",
17807
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
17808
+ lineHeight: 1.1,
17809
+ letterSpacing: "-0.025em",
17810
+ color: "var(--brand-text)",
17811
+ marginBottom: "1rem"
17812
+ },
17813
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
17814
+ children: title
17815
+ }
17816
+ ),
17817
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17818
+ "p",
17819
+ {
17820
+ style: {
17821
+ fontFamily: "var(--brand-font-body)",
17822
+ fontSize: "1rem",
17823
+ lineHeight: 1.7,
17824
+ fontWeight: 300,
17825
+ color: "var(--brand-text-muted)",
17826
+ maxWidth: "340px"
17827
+ },
17828
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
17829
+ children: "This page doesn't have any content yet."
17830
+ }
17831
+ )
17832
+ ] });
17833
+ }
17762
17834
  // Annotate the CommonJS export names for ESM import in node:
17763
17835
  0 && (module.exports = {
17764
17836
  AI_DEFAULT_BRAND,
@@ -17776,6 +17848,7 @@ function OhhwellsBridge() {
17776
17848
  DropdownMenuItem,
17777
17849
  DropdownMenuSeparator,
17778
17850
  DropdownMenuTrigger,
17851
+ EmptySection,
17779
17852
  ItemActionToolbar,
17780
17853
  ItemInteractionLayer,
17781
17854
  LinkEditorPanel,