@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.d.cts CHANGED
@@ -512,6 +512,16 @@ declare function TooltipContent({ className, sideOffset, side, children, hideArr
512
512
  hideArrow?: boolean;
513
513
  }): react_jsx_runtime.JSX.Element;
514
514
 
515
+ interface EmptySectionProps {
516
+ title: string;
517
+ homeHref?: string;
518
+ /** data-ohw-key values for the editable text nodes — omit a key to leave that node non-editable. */
519
+ eyebrowKey?: string;
520
+ titleKey?: string;
521
+ subtitleKey?: string;
522
+ }
523
+ declare function EmptySection({ title, homeHref, eyebrowKey, titleKey, subtitleKey }: EmptySectionProps): react_jsx_runtime.JSX.Element;
524
+
515
525
  declare function isEditSessionActive(): boolean;
516
526
 
517
527
  type CarouselSlide = {
@@ -535,4 +545,4 @@ declare function useOhwCarousel(key: string, initial: CarouselSlide[]): {
535
545
  bind: Record<string, string>;
536
546
  };
537
547
 
538
- export { AI_DEFAULT_BRAND, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, buildTarget, dropIndicatorVariants, filterAvailablePages, getEditModeInitialState, isEditSessionActive, isRenderableTree, isValidUrl, parseTarget, toggleVariants, useOhwCarousel, validateUrlInput };
548
+ export { AI_DEFAULT_BRAND, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, buildTarget, dropIndicatorVariants, filterAvailablePages, getEditModeInitialState, isEditSessionActive, isRenderableTree, isValidUrl, parseTarget, toggleVariants, useOhwCarousel, validateUrlInput };
package/dist/index.d.ts CHANGED
@@ -512,6 +512,16 @@ declare function TooltipContent({ className, sideOffset, side, children, hideArr
512
512
  hideArrow?: boolean;
513
513
  }): react_jsx_runtime.JSX.Element;
514
514
 
515
+ interface EmptySectionProps {
516
+ title: string;
517
+ homeHref?: string;
518
+ /** data-ohw-key values for the editable text nodes — omit a key to leave that node non-editable. */
519
+ eyebrowKey?: string;
520
+ titleKey?: string;
521
+ subtitleKey?: string;
522
+ }
523
+ declare function EmptySection({ title, homeHref, eyebrowKey, titleKey, subtitleKey }: EmptySectionProps): react_jsx_runtime.JSX.Element;
524
+
515
525
  declare function isEditSessionActive(): boolean;
516
526
 
517
527
  type CarouselSlide = {
@@ -535,4 +545,4 @@ declare function useOhwCarousel(key: string, initial: CarouselSlide[]): {
535
545
  bind: Record<string, string>;
536
546
  };
537
547
 
538
- export { AI_DEFAULT_BRAND, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, buildTarget, dropIndicatorVariants, filterAvailablePages, getEditModeInitialState, isEditSessionActive, isRenderableTree, isValidUrl, parseTarget, toggleVariants, useOhwCarousel, validateUrlInput };
548
+ export { AI_DEFAULT_BRAND, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, buildTarget, dropIndicatorVariants, filterAvailablePages, getEditModeInitialState, isEditSessionActive, isRenderableTree, isValidUrl, parseTarget, toggleVariants, useOhwCarousel, validateUrlInput };
package/dist/index.js CHANGED
@@ -19551,21 +19551,10 @@ function parseSchedulingInsertAfter(insertAfter) {
19551
19551
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
19552
19552
  };
19553
19553
  }
19554
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
19555
- const parsed = parseSchedulingInsertAfter(insertAfter);
19556
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
19557
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
19558
- return { effectiveInsertAfter, insertBefore };
19559
- }
19560
- function getSchedulingMountPoint(insertAfter) {
19561
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
19562
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
19563
- if (!anchorEl && anchor === "scheduling") {
19564
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
19565
- anchorEl = widgets.at(-1) ?? null;
19566
- }
19567
- if (!anchorEl) return null;
19568
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
19554
+ function resolveEntryAnchor(entry) {
19555
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
19556
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
19557
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
19569
19558
  }
19570
19559
  function schedulingMountDepth(insertAfter) {
19571
19560
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -19582,8 +19571,7 @@ function getPageSchedulingEntries(raw) {
19582
19571
  }
19583
19572
  }
19584
19573
  function isSchedulingWidgetMissing(entry) {
19585
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
19586
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
19574
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
19587
19575
  }
19588
19576
  function hasMissingSchedulingWidgets(entries) {
19589
19577
  return entries.some(isSchedulingWidgetMissing);
@@ -19621,17 +19609,18 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
19621
19609
  } catch {
19622
19610
  }
19623
19611
  }
19624
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
19625
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
19626
- const sectionId = schedulingSectionId(effectiveInsertAfter);
19612
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
19613
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
19614
+ const sectionId = schedulingSectionId(widgetId);
19627
19615
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
19628
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
19629
- if (!mountPoint) return false;
19616
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
19617
+ if (!anchorEl) return false;
19618
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
19630
19619
  const container = document.createElement("div");
19631
19620
  container.dataset.ohwSectionContainer = "scheduling";
19632
19621
  container.dataset.ohwInstance = sectionId;
19633
- if (insertBefore) {
19634
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
19622
+ if (beforeId) {
19623
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
19635
19624
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
19636
19625
  if (!beforePoint) return false;
19637
19626
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -19642,20 +19631,26 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
19642
19631
  }
19643
19632
  tail.insertAdjacentElement("afterend", container);
19644
19633
  }
19645
- const root = createRoot2(container);
19646
- schedulingRoots.set(container, root);
19647
- flushSync2(() => {
19648
- root.render(
19649
- /* @__PURE__ */ jsx36(
19650
- SchedulingWidget,
19651
- {
19652
- notifyOnConnect,
19653
- initialScheduleId: scheduleId,
19654
- insertAfter: effectiveInsertAfter
19655
- }
19656
- )
19657
- );
19658
- });
19634
+ try {
19635
+ const root = createRoot2(container);
19636
+ schedulingRoots.set(container, root);
19637
+ flushSync2(() => {
19638
+ root.render(
19639
+ /* @__PURE__ */ jsx36(
19640
+ SchedulingWidget,
19641
+ {
19642
+ notifyOnConnect,
19643
+ initialScheduleId: scheduleId,
19644
+ insertAfter: widgetId
19645
+ }
19646
+ )
19647
+ );
19648
+ });
19649
+ } catch (err) {
19650
+ console.error("[ow:scheduling] render threw", err);
19651
+ container.remove();
19652
+ return false;
19653
+ }
19659
19654
  const tracker = getSectionsTracker();
19660
19655
  let sections = [];
19661
19656
  try {
@@ -19663,10 +19658,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
19663
19658
  } catch {
19664
19659
  }
19665
19660
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
19666
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
19661
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
19667
19662
  sections.push({
19668
19663
  type: "scheduling",
19669
- insertAfter: effectiveInsertAfter,
19664
+ insertAfter: widgetId,
19665
+ anchorId,
19666
+ beforeId: beforeId ?? null,
19670
19667
  pagePath: window.location.pathname,
19671
19668
  ...scheduleId ? { scheduleId } : {}
19672
19669
  });
@@ -19680,7 +19677,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
19680
19677
  for (let i = pending.length - 1; i >= 0; i--) {
19681
19678
  const entry = pending[i];
19682
19679
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
19683
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
19680
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
19681
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
19684
19682
  pending.splice(i, 1);
19685
19683
  }
19686
19684
  }
@@ -20743,6 +20741,7 @@ function StateToggle({
20743
20741
  );
20744
20742
  }
20745
20743
  var contentCache = /* @__PURE__ */ new Map();
20744
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
20746
20745
  var brandingCache = /* @__PURE__ */ new Map();
20747
20746
  var OHW_LOADER_STYLE = {
20748
20747
  position: "fixed",
@@ -20949,6 +20948,7 @@ function OhhwellsBridge() {
20949
20948
  if (!subdomainFromQuery || typeof window === "undefined") return;
20950
20949
  const url = new URL(window.location.href);
20951
20950
  if (!url.searchParams.has("subdomain")) return;
20951
+ if (!resolveSubdomain(null)) return;
20952
20952
  url.searchParams.delete("subdomain");
20953
20953
  window.history.replaceState(null, "", url.pathname + url.search + url.hash);
20954
20954
  }, [subdomainFromQuery]);
@@ -22995,7 +22995,9 @@ function OhhwellsBridge() {
22995
22995
  let cancelled = false;
22996
22996
  setFetchState("loading");
22997
22997
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
22998
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
22998
+ const initialPath = pathname;
22999
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
23000
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
22999
23001
  if (cancelled) return;
23000
23002
  const content = data?.content ?? {};
23001
23003
  const branding = Boolean(data?.showBranding);
@@ -23192,6 +23194,17 @@ function OhhwellsBridge() {
23192
23194
  debounceTimer = setTimeout(applyFromCache, 150);
23193
23195
  };
23194
23196
  applyFromCache();
23197
+ const pathCacheKey = `${subdomain}::${pathname}`;
23198
+ if (!fetchedContentPaths.has(pathCacheKey)) {
23199
+ fetchedContentPaths.add(pathCacheKey);
23200
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
23201
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
23202
+ if (!data?.content) return;
23203
+ contentCache.set(subdomain, data.content);
23204
+ applyFromCache();
23205
+ }).catch(() => {
23206
+ });
23207
+ }
23195
23208
  observer = new MutationObserver(scheduleApply);
23196
23209
  observer.observe(document.body, { childList: true, subtree: true });
23197
23210
  return () => {
@@ -26078,8 +26091,12 @@ function OhhwellsBridge() {
26078
26091
  if (inserted) {
26079
26092
  const tracker = getSectionsTracker();
26080
26093
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
26081
- const h = document.body.scrollHeight;
26082
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
26094
+ const reportHeight = () => {
26095
+ const h = document.body.scrollHeight;
26096
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
26097
+ };
26098
+ reportHeight();
26099
+ setTimeout(reportHeight, 500);
26083
26100
  }
26084
26101
  };
26085
26102
  const handleSwitchSchedule = (e) => {
@@ -26805,7 +26822,7 @@ function OhhwellsBridge() {
26805
26822
  postToParent2({
26806
26823
  type: "ow:ready",
26807
26824
  version: "1",
26808
- bridgeVersion: "0.1.123",
26825
+ bridgeVersion: "0.1.124",
26809
26826
  path: pathname,
26810
26827
  nodes: collectEditableNodes(editContentRef.current),
26811
26828
  sections
@@ -27892,6 +27909,59 @@ function OhhwellsBridge() {
27892
27909
  ) : null
27893
27910
  ] });
27894
27911
  }
27912
+
27913
+ // src/ui/EmptySection.tsx
27914
+ import Link3 from "next/link";
27915
+ import { Fragment as Fragment11, jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
27916
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
27917
+ return /* @__PURE__ */ jsxs24(Fragment11, { children: [
27918
+ /* @__PURE__ */ jsx37(
27919
+ "p",
27920
+ {
27921
+ style: {
27922
+ fontFamily: "var(--brand-font-body)",
27923
+ fontSize: "0.75rem",
27924
+ fontWeight: 500,
27925
+ letterSpacing: "0.15em",
27926
+ textTransform: "uppercase",
27927
+ color: "var(--brand-accent)",
27928
+ marginBottom: "1.5rem"
27929
+ },
27930
+ children: /* @__PURE__ */ jsx37(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx37("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
27931
+ }
27932
+ ),
27933
+ /* @__PURE__ */ jsx37(
27934
+ "h1",
27935
+ {
27936
+ style: {
27937
+ fontFamily: "var(--brand-font-heading)",
27938
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
27939
+ lineHeight: 1.1,
27940
+ letterSpacing: "-0.025em",
27941
+ color: "var(--brand-text)",
27942
+ marginBottom: "1rem"
27943
+ },
27944
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
27945
+ children: title
27946
+ }
27947
+ ),
27948
+ /* @__PURE__ */ jsx37(
27949
+ "p",
27950
+ {
27951
+ style: {
27952
+ fontFamily: "var(--brand-font-body)",
27953
+ fontSize: "1rem",
27954
+ lineHeight: 1.7,
27955
+ fontWeight: 300,
27956
+ color: "var(--brand-text-muted)",
27957
+ maxWidth: "340px"
27958
+ },
27959
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
27960
+ children: "This page doesn't have any content yet."
27961
+ }
27962
+ )
27963
+ ] });
27964
+ }
27895
27965
  export {
27896
27966
  AI_DEFAULT_BRAND,
27897
27967
  AI_TREE_SCHEMA_VERSIONS,
@@ -27908,6 +27978,7 @@ export {
27908
27978
  DropdownMenuItem,
27909
27979
  DropdownMenuSeparator,
27910
27980
  DropdownMenuTrigger,
27981
+ EmptySection,
27911
27982
  ItemActionToolbar,
27912
27983
  ItemInteractionLayer,
27913
27984
  LinkEditorPanel,