@ohhwells/bridge 0.1.155 → 0.1.157
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 +192 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -1
- package/dist/index.d.ts +31 -1
- package/dist/index.js +190 -139
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -926,6 +926,36 @@ declare function shouldFetchContentForPath(subdomain: string, path: string): boo
|
|
|
926
926
|
/** Test seam: the cache is module state that would otherwise leak between cases. */
|
|
927
927
|
declare function resetOverriddenPathsCache(): void;
|
|
928
928
|
|
|
929
|
+
/** One entry of the parent's `ow:site-pages` payload (title is unused here). */
|
|
930
|
+
type SitePageEntry = {
|
|
931
|
+
path: string;
|
|
932
|
+
kind?: string | null;
|
|
933
|
+
collection?: string | null;
|
|
934
|
+
};
|
|
935
|
+
type CollectionItemHit = {
|
|
936
|
+
/** The page path as the editor knows it, not as the anchor spelled it. */
|
|
937
|
+
path: string;
|
|
938
|
+
/** Route segment of the owning collection, e.g. `blog`. */
|
|
939
|
+
collection: string;
|
|
940
|
+
};
|
|
941
|
+
/** Normalized item path → the hit to post. Empty on a site with no collections. */
|
|
942
|
+
type CollectionItemIndex = Map<string, CollectionItemHit>;
|
|
943
|
+
declare function indexCollectionItems(pages: readonly SitePageEntry[]): CollectionItemIndex;
|
|
944
|
+
/**
|
|
945
|
+
* Resolve an edit-mode click to the collection item it belongs to, or null to leave the click
|
|
946
|
+
* alone.
|
|
947
|
+
*
|
|
948
|
+
* Only clicks on *editable text* are intercepted. Media inside a post card stays replaceable
|
|
949
|
+
* from the listing page, so this removes exactly the inline text editing the Post panel
|
|
950
|
+
* replaces and nothing else.
|
|
951
|
+
*
|
|
952
|
+
* A CTA link is left alone too. `professional-services` links each `/team/${slug}` from a
|
|
953
|
+
* `data-ohw-role="button"` "View profile →" anchor rather than wrapping the whole card, and that
|
|
954
|
+
* label is the listing page's own copy — editing it has nothing to do with the item it points at.
|
|
955
|
+
* The role is the site tagger's CTA marker (see `cta-button.ts`), so this holds on any template.
|
|
956
|
+
*/
|
|
957
|
+
declare function matchCollectionItemClick(target: Element | null, index: CollectionItemIndex): CollectionItemHit | null;
|
|
958
|
+
|
|
929
959
|
declare function isStickySearchParam(name: string): boolean;
|
|
930
960
|
/**
|
|
931
961
|
* Copy the sticky params of `currentSearch` onto `target`, and report whether anything changed.
|
|
@@ -970,4 +1000,4 @@ declare function useOhwCarousel(key: string, initial: CarouselSlide[]): {
|
|
|
970
1000
|
bind: Record<string, string>;
|
|
971
1001
|
};
|
|
972
1002
|
|
|
973
|
-
export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, BRAND_TITLE_KEY, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OHW_LOADER_PREHYDRATE_SCRIPT, OHW_LOADER_STYLE, OhhwellsBridge, OhwLoaderSpinner, OhwLoaderSurface, SITE_OG_IMAGE_KEY, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, applyBrandChrome, applyStickySearchParams, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, foldAlignIntoTrees, getEditModeInitialState, hasMountedContentNodes, insertFooterHours, isEditSessionActive, isPlainLeftClick, isRenderableFooterTree, isRenderableTree, isStickySearchParam, isValidUrl, nextViableSlide, parseAiFooterState, parseAiSectionsState, parseTarget, recordOverriddenPaths, removeFooterBlock, resetOverriddenPathsCache, resolveFooterCopyright, resolveStickyNavigation, serializeAiFooterState, shouldFetchContentForPath, stepViableSlide, stripTextAlignCss, toggleVariants, useOhwCarousel, validateUrlInput, viableSlides };
|
|
1003
|
+
export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, BRAND_TITLE_KEY, type CarouselSlide, type CollectionItemHit, type CollectionItemIndex, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OHW_LOADER_PREHYDRATE_SCRIPT, OHW_LOADER_STYLE, OhhwellsBridge, OhwLoaderSpinner, OhwLoaderSurface, SITE_OG_IMAGE_KEY, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, type SitePageEntry, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, applyBrandChrome, applyStickySearchParams, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, foldAlignIntoTrees, getEditModeInitialState, hasMountedContentNodes, indexCollectionItems, insertFooterHours, isEditSessionActive, isPlainLeftClick, isRenderableFooterTree, isRenderableTree, isStickySearchParam, isValidUrl, matchCollectionItemClick, nextViableSlide, parseAiFooterState, parseAiSectionsState, parseTarget, recordOverriddenPaths, removeFooterBlock, resetOverriddenPathsCache, resolveFooterCopyright, resolveStickyNavigation, serializeAiFooterState, shouldFetchContentForPath, stepViableSlide, stripTextAlignCss, toggleVariants, useOhwCarousel, validateUrlInput, viableSlides };
|
package/dist/index.d.ts
CHANGED
|
@@ -926,6 +926,36 @@ declare function shouldFetchContentForPath(subdomain: string, path: string): boo
|
|
|
926
926
|
/** Test seam: the cache is module state that would otherwise leak between cases. */
|
|
927
927
|
declare function resetOverriddenPathsCache(): void;
|
|
928
928
|
|
|
929
|
+
/** One entry of the parent's `ow:site-pages` payload (title is unused here). */
|
|
930
|
+
type SitePageEntry = {
|
|
931
|
+
path: string;
|
|
932
|
+
kind?: string | null;
|
|
933
|
+
collection?: string | null;
|
|
934
|
+
};
|
|
935
|
+
type CollectionItemHit = {
|
|
936
|
+
/** The page path as the editor knows it, not as the anchor spelled it. */
|
|
937
|
+
path: string;
|
|
938
|
+
/** Route segment of the owning collection, e.g. `blog`. */
|
|
939
|
+
collection: string;
|
|
940
|
+
};
|
|
941
|
+
/** Normalized item path → the hit to post. Empty on a site with no collections. */
|
|
942
|
+
type CollectionItemIndex = Map<string, CollectionItemHit>;
|
|
943
|
+
declare function indexCollectionItems(pages: readonly SitePageEntry[]): CollectionItemIndex;
|
|
944
|
+
/**
|
|
945
|
+
* Resolve an edit-mode click to the collection item it belongs to, or null to leave the click
|
|
946
|
+
* alone.
|
|
947
|
+
*
|
|
948
|
+
* Only clicks on *editable text* are intercepted. Media inside a post card stays replaceable
|
|
949
|
+
* from the listing page, so this removes exactly the inline text editing the Post panel
|
|
950
|
+
* replaces and nothing else.
|
|
951
|
+
*
|
|
952
|
+
* A CTA link is left alone too. `professional-services` links each `/team/${slug}` from a
|
|
953
|
+
* `data-ohw-role="button"` "View profile →" anchor rather than wrapping the whole card, and that
|
|
954
|
+
* label is the listing page's own copy — editing it has nothing to do with the item it points at.
|
|
955
|
+
* The role is the site tagger's CTA marker (see `cta-button.ts`), so this holds on any template.
|
|
956
|
+
*/
|
|
957
|
+
declare function matchCollectionItemClick(target: Element | null, index: CollectionItemIndex): CollectionItemHit | null;
|
|
958
|
+
|
|
929
959
|
declare function isStickySearchParam(name: string): boolean;
|
|
930
960
|
/**
|
|
931
961
|
* Copy the sticky params of `currentSearch` onto `target`, and report whether anything changed.
|
|
@@ -970,4 +1000,4 @@ declare function useOhwCarousel(key: string, initial: CarouselSlide[]): {
|
|
|
970
1000
|
bind: Record<string, string>;
|
|
971
1001
|
};
|
|
972
1002
|
|
|
973
|
-
export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, BRAND_TITLE_KEY, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OHW_LOADER_PREHYDRATE_SCRIPT, OHW_LOADER_STYLE, OhhwellsBridge, OhwLoaderSpinner, OhwLoaderSurface, SITE_OG_IMAGE_KEY, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, applyBrandChrome, applyStickySearchParams, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, foldAlignIntoTrees, getEditModeInitialState, hasMountedContentNodes, insertFooterHours, isEditSessionActive, isPlainLeftClick, isRenderableFooterTree, isRenderableTree, isStickySearchParam, isValidUrl, nextViableSlide, parseAiFooterState, parseAiSectionsState, parseTarget, recordOverriddenPaths, removeFooterBlock, resetOverriddenPathsCache, resolveFooterCopyright, resolveStickyNavigation, serializeAiFooterState, shouldFetchContentForPath, stepViableSlide, stripTextAlignCss, toggleVariants, useOhwCarousel, validateUrlInput, viableSlides };
|
|
1003
|
+
export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, BRAND_TITLE_KEY, type CarouselSlide, type CollectionItemHit, type CollectionItemIndex, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OHW_LOADER_PREHYDRATE_SCRIPT, OHW_LOADER_STYLE, OhhwellsBridge, OhwLoaderSpinner, OhwLoaderSurface, SITE_OG_IMAGE_KEY, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, type SitePageEntry, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, applyBrandChrome, applyStickySearchParams, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, foldAlignIntoTrees, getEditModeInitialState, hasMountedContentNodes, indexCollectionItems, insertFooterHours, isEditSessionActive, isPlainLeftClick, isRenderableFooterTree, isRenderableTree, isStickySearchParam, isValidUrl, matchCollectionItemClick, nextViableSlide, parseAiFooterState, parseAiSectionsState, parseTarget, recordOverriddenPaths, removeFooterBlock, resetOverriddenPathsCache, resolveFooterCopyright, resolveStickyNavigation, serializeAiFooterState, shouldFetchContentForPath, stepViableSlide, stripTextAlignCss, toggleVariants, useOhwCarousel, validateUrlInput, viableSlides };
|
package/dist/index.js
CHANGED
|
@@ -11585,6 +11585,172 @@ function useLinkHrefGuardian(...deps) {
|
|
|
11585
11585
|
}, deps);
|
|
11586
11586
|
}
|
|
11587
11587
|
|
|
11588
|
+
// src/ui/link-modal/linkModal.utils.ts
|
|
11589
|
+
function parseTarget(target) {
|
|
11590
|
+
if (!target) return { pageRoute: "", sectionId: null };
|
|
11591
|
+
const hashIndex = target.indexOf("#");
|
|
11592
|
+
if (hashIndex === -1) return { pageRoute: target, sectionId: null };
|
|
11593
|
+
return {
|
|
11594
|
+
pageRoute: target.slice(0, hashIndex),
|
|
11595
|
+
sectionId: target.slice(hashIndex + 1) || null
|
|
11596
|
+
};
|
|
11597
|
+
}
|
|
11598
|
+
function normalizePath(path) {
|
|
11599
|
+
const trimmed = path.trim();
|
|
11600
|
+
if (!trimmed) return "/";
|
|
11601
|
+
try {
|
|
11602
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
11603
|
+
return new URL(trimmed).pathname || "/";
|
|
11604
|
+
}
|
|
11605
|
+
} catch {
|
|
11606
|
+
}
|
|
11607
|
+
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
11608
|
+
if (withSlash.length > 1 && withSlash.endsWith("/")) return withSlash.slice(0, -1);
|
|
11609
|
+
return withSlash;
|
|
11610
|
+
}
|
|
11611
|
+
function findPageByPath(pages, path) {
|
|
11612
|
+
const normalized = normalizePath(path);
|
|
11613
|
+
return pages.find((p) => normalizePath(p.path) === normalized);
|
|
11614
|
+
}
|
|
11615
|
+
function inferPageTitleFromPath(path) {
|
|
11616
|
+
const normalized = normalizePath(path);
|
|
11617
|
+
if (normalized === "/") return "Home";
|
|
11618
|
+
const segment = normalized.split("/").filter(Boolean).pop() ?? "";
|
|
11619
|
+
if (!segment) return "Home";
|
|
11620
|
+
return segment.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
11621
|
+
}
|
|
11622
|
+
function isInternalPath(route) {
|
|
11623
|
+
const trimmed = route.trim();
|
|
11624
|
+
if (!trimmed) return false;
|
|
11625
|
+
if (/^https?:\/\//i.test(trimmed)) return false;
|
|
11626
|
+
if (trimmed.startsWith("/")) return true;
|
|
11627
|
+
if (trimmed.includes(" ") || trimmed.includes(".")) return false;
|
|
11628
|
+
return true;
|
|
11629
|
+
}
|
|
11630
|
+
function resolvePage(route, pages) {
|
|
11631
|
+
const found = findPageByPath(pages, route);
|
|
11632
|
+
if (found) return { path: found.path, title: found.title };
|
|
11633
|
+
return { path: normalizePath(route), title: inferPageTitleFromPath(route) };
|
|
11634
|
+
}
|
|
11635
|
+
function getSectionsForPath(sectionsByPath, pageRoute) {
|
|
11636
|
+
if (!sectionsByPath) return [];
|
|
11637
|
+
const normalized = normalizePath(pageRoute);
|
|
11638
|
+
return sectionsByPath[normalized] ?? sectionsByPath[pageRoute] ?? [];
|
|
11639
|
+
}
|
|
11640
|
+
function buildTarget(page, section) {
|
|
11641
|
+
if (section) return `${page.path}#${section.id}`;
|
|
11642
|
+
return page.path;
|
|
11643
|
+
}
|
|
11644
|
+
function isValidUrl(urlString) {
|
|
11645
|
+
try {
|
|
11646
|
+
const url = new URL(urlString);
|
|
11647
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
11648
|
+
const hostname = url.hostname;
|
|
11649
|
+
if (!hostname || hostname.length === 0) return false;
|
|
11650
|
+
if (hostname.includes(" ")) return false;
|
|
11651
|
+
if (hostname === "localhost") return true;
|
|
11652
|
+
if (!hostname.includes(".")) return false;
|
|
11653
|
+
if (!/[a-z]/i.test(hostname)) return false;
|
|
11654
|
+
return true;
|
|
11655
|
+
} catch {
|
|
11656
|
+
return false;
|
|
11657
|
+
}
|
|
11658
|
+
}
|
|
11659
|
+
function normalizeUrl(value) {
|
|
11660
|
+
const trimmed = value.trim();
|
|
11661
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) return trimmed;
|
|
11662
|
+
return `https://${trimmed}`;
|
|
11663
|
+
}
|
|
11664
|
+
function validateUrlInput(value, pages, allPages) {
|
|
11665
|
+
const trimmed = value.trim();
|
|
11666
|
+
if (!trimmed) return "";
|
|
11667
|
+
const exactPage = allPages.find((p) => p.title.toLowerCase() === trimmed.toLowerCase());
|
|
11668
|
+
if (exactPage) return "";
|
|
11669
|
+
const hasPrefixMatch = pages.some((p) => p.title.toLowerCase().startsWith(trimmed.toLowerCase()));
|
|
11670
|
+
if (hasPrefixMatch) {
|
|
11671
|
+
return "Select a page from the list or enter a valid URL";
|
|
11672
|
+
}
|
|
11673
|
+
const hasScheme = /^https?:\/\//i.test(trimmed);
|
|
11674
|
+
if (!hasScheme && !trimmed.includes(".")) {
|
|
11675
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
11676
|
+
}
|
|
11677
|
+
if (!isValidUrl(normalizeUrl(trimmed))) {
|
|
11678
|
+
return "Please enter a valid URL (e.g., https://example.com)";
|
|
11679
|
+
}
|
|
11680
|
+
return "";
|
|
11681
|
+
}
|
|
11682
|
+
function getEditModeInitialState(target, pages, sections) {
|
|
11683
|
+
const empty = {
|
|
11684
|
+
searchValue: "",
|
|
11685
|
+
selectedPage: null,
|
|
11686
|
+
selectedSection: null,
|
|
11687
|
+
step: "input"
|
|
11688
|
+
};
|
|
11689
|
+
if (!target) return empty;
|
|
11690
|
+
if (isValidUrl(normalizeUrl(target))) {
|
|
11691
|
+
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
11692
|
+
}
|
|
11693
|
+
const { pageRoute, sectionId } = parseTarget(target);
|
|
11694
|
+
if (sectionId) {
|
|
11695
|
+
if (isInternalPath(pageRoute)) {
|
|
11696
|
+
const page = resolvePage(pageRoute, pages);
|
|
11697
|
+
const sec = sections.find((s) => s.id === sectionId);
|
|
11698
|
+
return {
|
|
11699
|
+
searchValue: page.title,
|
|
11700
|
+
selectedPage: page,
|
|
11701
|
+
selectedSection: sec ? { id: sec.id, label: sec.label } : null,
|
|
11702
|
+
step: "input"
|
|
11703
|
+
};
|
|
11704
|
+
}
|
|
11705
|
+
return { searchValue: pageRoute || target, selectedPage: null, selectedSection: null, step: "input" };
|
|
11706
|
+
}
|
|
11707
|
+
if (isInternalPath(pageRoute)) {
|
|
11708
|
+
const page = resolvePage(pageRoute, pages);
|
|
11709
|
+
return {
|
|
11710
|
+
searchValue: page.title,
|
|
11711
|
+
selectedPage: page,
|
|
11712
|
+
selectedSection: null,
|
|
11713
|
+
step: "input"
|
|
11714
|
+
};
|
|
11715
|
+
}
|
|
11716
|
+
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
11717
|
+
}
|
|
11718
|
+
function filterAvailablePages(pages, existingTargets) {
|
|
11719
|
+
const taken = new Set(existingTargets);
|
|
11720
|
+
return pages.filter((p) => !taken.has(p.path));
|
|
11721
|
+
}
|
|
11722
|
+
|
|
11723
|
+
// src/lib/collection-item-click.ts
|
|
11724
|
+
function indexCollectionItems(pages) {
|
|
11725
|
+
const index = /* @__PURE__ */ new Map();
|
|
11726
|
+
for (const page of pages) {
|
|
11727
|
+
if (page.kind !== "COLLECTION_ITEM" || !page.collection || !page.path) continue;
|
|
11728
|
+
index.set(normalizePath(page.path), { path: page.path, collection: page.collection });
|
|
11729
|
+
}
|
|
11730
|
+
return index;
|
|
11731
|
+
}
|
|
11732
|
+
function pathOfHref(href) {
|
|
11733
|
+
const trimmed = href.trim();
|
|
11734
|
+
if (!trimmed || trimmed.startsWith("#")) return null;
|
|
11735
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) && !/^https?:/i.test(trimmed)) return null;
|
|
11736
|
+
const withoutHash = trimmed.split("#")[0];
|
|
11737
|
+
const withoutQuery = withoutHash.split("?")[0];
|
|
11738
|
+
if (!withoutQuery) return null;
|
|
11739
|
+
return normalizePath(withoutQuery);
|
|
11740
|
+
}
|
|
11741
|
+
function matchCollectionItemClick(target, index) {
|
|
11742
|
+
if (!target || index.size === 0) return null;
|
|
11743
|
+
const text = target.closest('[data-ohw-editable="text"], [data-ohw-editable="plain"]');
|
|
11744
|
+
if (!text) return null;
|
|
11745
|
+
const anchor = text.closest("a");
|
|
11746
|
+
if (!anchor || anchor.closest(CTA_BUTTON_SELECTOR)) return null;
|
|
11747
|
+
const href = anchor.getAttribute("href");
|
|
11748
|
+
if (!href) return null;
|
|
11749
|
+
const path = pathOfHref(href);
|
|
11750
|
+
if (!path) return null;
|
|
11751
|
+
return index.get(path) ?? null;
|
|
11752
|
+
}
|
|
11753
|
+
|
|
11588
11754
|
// src/lib/sticky-search-params.ts
|
|
11589
11755
|
var STICKY_EXACT = /* @__PURE__ */ new Set([
|
|
11590
11756
|
// Site identity. Required in local dev and in the canvas editor's iframe, where there is no
|
|
@@ -16934,7 +17100,7 @@ function apiBase() {
|
|
|
16934
17100
|
function bridgeVersion() {
|
|
16935
17101
|
try {
|
|
16936
17102
|
if (true) {
|
|
16937
|
-
return "0.1.
|
|
17103
|
+
return "0.1.157";
|
|
16938
17104
|
}
|
|
16939
17105
|
} catch {
|
|
16940
17106
|
}
|
|
@@ -16948,7 +17114,7 @@ function stampBridgeVersionOnDocument() {
|
|
|
16948
17114
|
return;
|
|
16949
17115
|
}
|
|
16950
17116
|
try {
|
|
16951
|
-
const v = true ? "0.1.
|
|
17117
|
+
const v = true ? "0.1.157" : "";
|
|
16952
17118
|
if (v) {
|
|
16953
17119
|
document.documentElement.setAttribute("data-ohw-bridge-version", v);
|
|
16954
17120
|
window.__OHW_BRIDGE_VERSION__ = v;
|
|
@@ -16956,6 +17122,7 @@ function stampBridgeVersionOnDocument() {
|
|
|
16956
17122
|
} catch {
|
|
16957
17123
|
}
|
|
16958
17124
|
}
|
|
17125
|
+
var TRANSIENT_CONTENT_FETCH = /^(Failed to fetch|Load failed|NetworkError when attempting to fetch resource\.?|fetch failed|The user aborted a request\.?|The operation was aborted\.?)$/i;
|
|
16959
17126
|
var RECOVERABLE_PAGE_ERROR = /Minified React error #(?:418|419|421|422|423|425)\b|Hydration failed because the initial UI does not match|Text content does not match server-rendered HTML|There was an error while hydrating|did not match\. Server:/i;
|
|
16960
17127
|
function isVercelPreviewHost() {
|
|
16961
17128
|
if (typeof location === "undefined") {
|
|
@@ -16973,6 +17140,9 @@ function reportOutageBeacon(args) {
|
|
|
16973
17140
|
if (args.reason === "page_error" && args.detail && RECOVERABLE_PAGE_ERROR.test(args.detail)) {
|
|
16974
17141
|
return;
|
|
16975
17142
|
}
|
|
17143
|
+
if (args.reason === "content_fetch_failed" && args.detail && TRANSIENT_CONTENT_FETCH.test(args.detail.trim())) {
|
|
17144
|
+
return;
|
|
17145
|
+
}
|
|
16976
17146
|
const key = `${args.subdomain}|${args.reason}`;
|
|
16977
17147
|
const now = Date.now();
|
|
16978
17148
|
const prev = lastSent.get(key) || 0;
|
|
@@ -20604,141 +20774,6 @@ function isEditSessionActive() {
|
|
|
20604
20774
|
return new URLSearchParams(q).get("mode") === "edit";
|
|
20605
20775
|
}
|
|
20606
20776
|
|
|
20607
|
-
// src/ui/link-modal/linkModal.utils.ts
|
|
20608
|
-
function parseTarget(target) {
|
|
20609
|
-
if (!target) return { pageRoute: "", sectionId: null };
|
|
20610
|
-
const hashIndex = target.indexOf("#");
|
|
20611
|
-
if (hashIndex === -1) return { pageRoute: target, sectionId: null };
|
|
20612
|
-
return {
|
|
20613
|
-
pageRoute: target.slice(0, hashIndex),
|
|
20614
|
-
sectionId: target.slice(hashIndex + 1) || null
|
|
20615
|
-
};
|
|
20616
|
-
}
|
|
20617
|
-
function normalizePath(path) {
|
|
20618
|
-
const trimmed = path.trim();
|
|
20619
|
-
if (!trimmed) return "/";
|
|
20620
|
-
try {
|
|
20621
|
-
if (/^https?:\/\//i.test(trimmed)) {
|
|
20622
|
-
return new URL(trimmed).pathname || "/";
|
|
20623
|
-
}
|
|
20624
|
-
} catch {
|
|
20625
|
-
}
|
|
20626
|
-
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
20627
|
-
if (withSlash.length > 1 && withSlash.endsWith("/")) return withSlash.slice(0, -1);
|
|
20628
|
-
return withSlash;
|
|
20629
|
-
}
|
|
20630
|
-
function findPageByPath(pages, path) {
|
|
20631
|
-
const normalized = normalizePath(path);
|
|
20632
|
-
return pages.find((p) => normalizePath(p.path) === normalized);
|
|
20633
|
-
}
|
|
20634
|
-
function inferPageTitleFromPath(path) {
|
|
20635
|
-
const normalized = normalizePath(path);
|
|
20636
|
-
if (normalized === "/") return "Home";
|
|
20637
|
-
const segment = normalized.split("/").filter(Boolean).pop() ?? "";
|
|
20638
|
-
if (!segment) return "Home";
|
|
20639
|
-
return segment.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
20640
|
-
}
|
|
20641
|
-
function isInternalPath(route) {
|
|
20642
|
-
const trimmed = route.trim();
|
|
20643
|
-
if (!trimmed) return false;
|
|
20644
|
-
if (/^https?:\/\//i.test(trimmed)) return false;
|
|
20645
|
-
if (trimmed.startsWith("/")) return true;
|
|
20646
|
-
if (trimmed.includes(" ") || trimmed.includes(".")) return false;
|
|
20647
|
-
return true;
|
|
20648
|
-
}
|
|
20649
|
-
function resolvePage(route, pages) {
|
|
20650
|
-
const found = findPageByPath(pages, route);
|
|
20651
|
-
if (found) return { path: found.path, title: found.title };
|
|
20652
|
-
return { path: normalizePath(route), title: inferPageTitleFromPath(route) };
|
|
20653
|
-
}
|
|
20654
|
-
function getSectionsForPath(sectionsByPath, pageRoute) {
|
|
20655
|
-
if (!sectionsByPath) return [];
|
|
20656
|
-
const normalized = normalizePath(pageRoute);
|
|
20657
|
-
return sectionsByPath[normalized] ?? sectionsByPath[pageRoute] ?? [];
|
|
20658
|
-
}
|
|
20659
|
-
function buildTarget(page, section) {
|
|
20660
|
-
if (section) return `${page.path}#${section.id}`;
|
|
20661
|
-
return page.path;
|
|
20662
|
-
}
|
|
20663
|
-
function isValidUrl(urlString) {
|
|
20664
|
-
try {
|
|
20665
|
-
const url = new URL(urlString);
|
|
20666
|
-
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
20667
|
-
const hostname = url.hostname;
|
|
20668
|
-
if (!hostname || hostname.length === 0) return false;
|
|
20669
|
-
if (hostname.includes(" ")) return false;
|
|
20670
|
-
if (hostname === "localhost") return true;
|
|
20671
|
-
if (!hostname.includes(".")) return false;
|
|
20672
|
-
if (!/[a-z]/i.test(hostname)) return false;
|
|
20673
|
-
return true;
|
|
20674
|
-
} catch {
|
|
20675
|
-
return false;
|
|
20676
|
-
}
|
|
20677
|
-
}
|
|
20678
|
-
function normalizeUrl(value) {
|
|
20679
|
-
const trimmed = value.trim();
|
|
20680
|
-
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) return trimmed;
|
|
20681
|
-
return `https://${trimmed}`;
|
|
20682
|
-
}
|
|
20683
|
-
function validateUrlInput(value, pages, allPages) {
|
|
20684
|
-
const trimmed = value.trim();
|
|
20685
|
-
if (!trimmed) return "";
|
|
20686
|
-
const exactPage = allPages.find((p) => p.title.toLowerCase() === trimmed.toLowerCase());
|
|
20687
|
-
if (exactPage) return "";
|
|
20688
|
-
const hasPrefixMatch = pages.some((p) => p.title.toLowerCase().startsWith(trimmed.toLowerCase()));
|
|
20689
|
-
if (hasPrefixMatch) {
|
|
20690
|
-
return "Select a page from the list or enter a valid URL";
|
|
20691
|
-
}
|
|
20692
|
-
const hasScheme = /^https?:\/\//i.test(trimmed);
|
|
20693
|
-
if (!hasScheme && !trimmed.includes(".")) {
|
|
20694
|
-
return "Please enter a valid URL (e.g., https://example.com)";
|
|
20695
|
-
}
|
|
20696
|
-
if (!isValidUrl(normalizeUrl(trimmed))) {
|
|
20697
|
-
return "Please enter a valid URL (e.g., https://example.com)";
|
|
20698
|
-
}
|
|
20699
|
-
return "";
|
|
20700
|
-
}
|
|
20701
|
-
function getEditModeInitialState(target, pages, sections) {
|
|
20702
|
-
const empty = {
|
|
20703
|
-
searchValue: "",
|
|
20704
|
-
selectedPage: null,
|
|
20705
|
-
selectedSection: null,
|
|
20706
|
-
step: "input"
|
|
20707
|
-
};
|
|
20708
|
-
if (!target) return empty;
|
|
20709
|
-
if (isValidUrl(normalizeUrl(target))) {
|
|
20710
|
-
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
20711
|
-
}
|
|
20712
|
-
const { pageRoute, sectionId } = parseTarget(target);
|
|
20713
|
-
if (sectionId) {
|
|
20714
|
-
if (isInternalPath(pageRoute)) {
|
|
20715
|
-
const page = resolvePage(pageRoute, pages);
|
|
20716
|
-
const sec = sections.find((s) => s.id === sectionId);
|
|
20717
|
-
return {
|
|
20718
|
-
searchValue: page.title,
|
|
20719
|
-
selectedPage: page,
|
|
20720
|
-
selectedSection: sec ? { id: sec.id, label: sec.label } : null,
|
|
20721
|
-
step: "input"
|
|
20722
|
-
};
|
|
20723
|
-
}
|
|
20724
|
-
return { searchValue: pageRoute || target, selectedPage: null, selectedSection: null, step: "input" };
|
|
20725
|
-
}
|
|
20726
|
-
if (isInternalPath(pageRoute)) {
|
|
20727
|
-
const page = resolvePage(pageRoute, pages);
|
|
20728
|
-
return {
|
|
20729
|
-
searchValue: page.title,
|
|
20730
|
-
selectedPage: page,
|
|
20731
|
-
selectedSection: null,
|
|
20732
|
-
step: "input"
|
|
20733
|
-
};
|
|
20734
|
-
}
|
|
20735
|
-
return { searchValue: target, selectedPage: null, selectedSection: null, step: "input" };
|
|
20736
|
-
}
|
|
20737
|
-
function filterAvailablePages(pages, existingTargets) {
|
|
20738
|
-
const taken = new Set(existingTargets);
|
|
20739
|
-
return pages.filter((p) => !taken.has(p.path));
|
|
20740
|
-
}
|
|
20741
|
-
|
|
20742
20777
|
// src/lib/prefetch-sections.ts
|
|
20743
20778
|
function buildPageFetchUrl(path) {
|
|
20744
20779
|
const normalized = normalizePath(path);
|
|
@@ -25903,6 +25938,7 @@ function OhhwellsBridge() {
|
|
|
25903
25938
|
const appliedDrawerRef = useRef14(null);
|
|
25904
25939
|
const pendingDeleteUndoRef = useRef14(null);
|
|
25905
25940
|
const [sitePages, setSitePages] = useState18([]);
|
|
25941
|
+
const collectionItemsRef = useRef14(/* @__PURE__ */ new Map());
|
|
25906
25942
|
const [sectionsByPath, setSectionsByPath] = useState18({});
|
|
25907
25943
|
const sectionsPrefetchGenRef = useRef14(0);
|
|
25908
25944
|
const setLinkPopoverRef = useRef14(setLinkPopover);
|
|
@@ -26046,10 +26082,12 @@ function OhhwellsBridge() {
|
|
|
26046
26082
|
const onSitePages = (e) => {
|
|
26047
26083
|
if (e.data?.type !== "ow:site-pages" || !Array.isArray(e.data.pages)) return;
|
|
26048
26084
|
if (useFixtures) return;
|
|
26049
|
-
const
|
|
26085
|
+
const incoming = e.data.pages;
|
|
26086
|
+
const mapped = incoming.map((p) => ({
|
|
26050
26087
|
path: p.path,
|
|
26051
26088
|
title: p.title
|
|
26052
26089
|
}));
|
|
26090
|
+
collectionItemsRef.current = indexCollectionItems(incoming);
|
|
26053
26091
|
setSitePages(mapped);
|
|
26054
26092
|
runSectionsPrefetchRef.current(mapped);
|
|
26055
26093
|
};
|
|
@@ -28469,6 +28507,17 @@ function OhhwellsBridge() {
|
|
|
28469
28507
|
}
|
|
28470
28508
|
if (isInsideFloatingPanel(target)) return;
|
|
28471
28509
|
if (target.closest("[data-ohw-form-toolbar]")) return;
|
|
28510
|
+
const collectionItem = matchCollectionItemClick(target, collectionItemsRef.current);
|
|
28511
|
+
if (collectionItem) {
|
|
28512
|
+
e.preventDefault();
|
|
28513
|
+
e.stopPropagation();
|
|
28514
|
+
postToParentRef.current({
|
|
28515
|
+
type: "ow:collection-item-selected",
|
|
28516
|
+
path: collectionItem.path,
|
|
28517
|
+
collection: collectionItem.collection
|
|
28518
|
+
});
|
|
28519
|
+
return;
|
|
28520
|
+
}
|
|
28472
28521
|
if (target.closest(
|
|
28473
28522
|
'[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
|
|
28474
28523
|
)) {
|
|
@@ -31889,7 +31938,7 @@ function OhhwellsBridge() {
|
|
|
31889
31938
|
postToParent2({
|
|
31890
31939
|
type: "ow:ready",
|
|
31891
31940
|
version: "1",
|
|
31892
|
-
bridgeVersion: "0.1.
|
|
31941
|
+
bridgeVersion: "0.1.157",
|
|
31893
31942
|
path: pathname,
|
|
31894
31943
|
nodes: collectEditableNodes(editContentRef.current),
|
|
31895
31944
|
sections
|
|
@@ -33114,6 +33163,7 @@ export {
|
|
|
33114
33163
|
foldAlignIntoTrees,
|
|
33115
33164
|
getEditModeInitialState,
|
|
33116
33165
|
hasMountedContentNodes,
|
|
33166
|
+
indexCollectionItems,
|
|
33117
33167
|
insertFooterHours,
|
|
33118
33168
|
isEditSessionActive,
|
|
33119
33169
|
isPlainLeftClick,
|
|
@@ -33121,6 +33171,7 @@ export {
|
|
|
33121
33171
|
isRenderableTree,
|
|
33122
33172
|
isStickySearchParam,
|
|
33123
33173
|
isValidUrl,
|
|
33174
|
+
matchCollectionItemClick,
|
|
33124
33175
|
nextViableSlide,
|
|
33125
33176
|
parseAiFooterState,
|
|
33126
33177
|
parseAiSectionsState,
|