@asteroidcms/core-utils 0.1.7 → 0.2.0

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/client.cjs CHANGED
@@ -9,6 +9,7 @@ var error = require('@apollo/client/link/error');
9
9
  var jsxRuntime = require('react/jsx-runtime');
10
10
  var reactDom = require('react-dom');
11
11
  var hljs = require('highlight.js/lib/common');
12
+ var navigation = require('next/navigation');
12
13
 
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
15
 
@@ -1734,12 +1735,516 @@ function extractHeadingsFromElement(root, options = {}) {
1734
1735
  });
1735
1736
  return out;
1736
1737
  }
1738
+ function setMeta(attr, key, content) {
1739
+ let element = document.head.querySelector(
1740
+ `meta[${attr}="${key}"]`
1741
+ );
1742
+ if (!element) {
1743
+ if (!content) return;
1744
+ element = document.createElement("meta");
1745
+ element.setAttribute(attr, key);
1746
+ document.head.appendChild(element);
1747
+ } else if (!content) {
1748
+ element.remove();
1749
+ return;
1750
+ }
1751
+ element.setAttribute("content", content);
1752
+ }
1753
+ function setCanonical(url) {
1754
+ let canonical = document.head.querySelector(
1755
+ 'link[rel="canonical"]'
1756
+ );
1757
+ if (!canonical) {
1758
+ canonical = document.createElement("link");
1759
+ canonical.rel = "canonical";
1760
+ document.head.appendChild(canonical);
1761
+ }
1762
+ canonical.href = url;
1763
+ }
1764
+ function JsonLd({ data }) {
1765
+ if (!data) return null;
1766
+ const html = JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1767
+ return /* @__PURE__ */ jsxRuntime.jsx(
1768
+ "script",
1769
+ {
1770
+ type: "application/ld+json",
1771
+ dangerouslySetInnerHTML: { __html: html }
1772
+ }
1773
+ );
1774
+ }
1775
+ function Seo({
1776
+ title,
1777
+ description,
1778
+ url,
1779
+ siteName,
1780
+ keywords,
1781
+ twitter,
1782
+ image,
1783
+ noindex
1784
+ }) {
1785
+ react.useEffect(() => {
1786
+ document.title = title;
1787
+ setMeta("name", "description", description);
1788
+ setCanonical(url);
1789
+ setMeta("property", "og:title", title);
1790
+ setMeta("property", "og:description", description);
1791
+ setMeta("property", "og:url", url);
1792
+ setMeta("property", "og:site_name", siteName || "");
1793
+ setMeta("name", "keywords", keywords || "");
1794
+ setMeta("name", "robots", noindex ? "noindex" : "index");
1795
+ setMeta("name", "twitter:card", image ? "summary_large_image" : "summary");
1796
+ setMeta("name", "twitter:title", title);
1797
+ setMeta("name", "twitter:description", description);
1798
+ setMeta("name", "twitter:site", twitter || "");
1799
+ setMeta("property", "og:image", image || "");
1800
+ setMeta("name", "twitter:image", image || "");
1801
+ }, [title, description, url, siteName, keywords, twitter, image, noindex]);
1802
+ return null;
1803
+ }
1804
+
1805
+ // src/seo/seo.builders.ts
1806
+ function applyTitleTemplate(config, title) {
1807
+ return config.titleTemplate ? config.titleTemplate(title) : `${title} | ${config.siteName}`;
1808
+ }
1809
+ function buildOgImageUrl(config, params) {
1810
+ if (config.getOgImageUrl) {
1811
+ return config.getOgImageUrl(params);
1812
+ }
1813
+ const palette = config.ogImage?.palette;
1814
+ if (!palette) return void 0;
1815
+ const apiPath = config.ogImage?.apiPath ?? "/api/og";
1816
+ const base = config.baseUrl.replace(/\/$/, "");
1817
+ const searchParams = new URLSearchParams({
1818
+ title: params.title,
1819
+ type: params.type ?? "article",
1820
+ siteName: config.siteName,
1821
+ bg: palette.background,
1822
+ fg: palette.foreground,
1823
+ accent: palette.accent
1824
+ });
1825
+ if (params.subtitle?.trim()) searchParams.set("subtitle", params.subtitle.trim());
1826
+ if (params.eyebrow?.trim()) searchParams.set("eyebrow", params.eyebrow.trim());
1827
+ if (palette.accentMuted) searchParams.set("accentMuted", palette.accentMuted);
1828
+ if (palette.mutedText) searchParams.set("muted", palette.mutedText);
1829
+ return `${base}${apiPath}?${searchParams.toString()}`;
1830
+ }
1831
+ function resolveArticleImage(post, config) {
1832
+ const featuredImage = config.cmsUrl ? cmsImage(post.featured_image, { cmsUrl: config.cmsUrl }) : "";
1833
+ if (featuredImage) return featuredImage;
1834
+ const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription;
1835
+ return buildOgImageUrl(config, {
1836
+ title: post.title,
1837
+ subtitle: description,
1838
+ eyebrow: config.contentLabel ?? "Article",
1839
+ type: "article"
1840
+ });
1841
+ }
1842
+ function buildArticleSeoValues(post, config, slug, options) {
1843
+ const articlePath = config.articlePath ?? "/blog";
1844
+ const url = `${config.baseUrl.replace(/\/$/, "")}${articlePath}/${slug}`;
1845
+ const description = post.meta_description?.trim() || post.description?.trim() || config.defaultDescription || `Read the latest from ${config.siteName}.`;
1846
+ return {
1847
+ title: applyTitleTemplate(config, post.title),
1848
+ siteName: config.siteName,
1849
+ twitter: config.twitter ?? "",
1850
+ description,
1851
+ url,
1852
+ keywords: config.defaultKeywords ?? post.title,
1853
+ image: resolveArticleImage(post, config),
1854
+ noindex: options?.noindex ?? config.noindex,
1855
+ manifestUrl: config.manifestUrl
1856
+ };
1857
+ }
1858
+ function buildArticleListingSeoValues(config, options) {
1859
+ const articlePath = config.articlePath ?? "/blog";
1860
+ const base = config.baseUrl.replace(/\/$/, "");
1861
+ const label = config.contentLabel ?? "Articles";
1862
+ const categoryName = options?.categoryName?.trim();
1863
+ const categorySlug = options?.categorySlug?.trim();
1864
+ const titleText = categoryName ? `${categoryName} ${label}` : label;
1865
+ const description = categoryName ? `Explore ${categoryName} ${label.toLowerCase()}, guides, and the latest updates from ${config.siteName}.` : config.defaultDescription || `Browse ${label.toLowerCase()}, insights, and the latest updates from ${config.siteName}.`;
1866
+ const url = categorySlug ? `${base}${articlePath}/category/${categorySlug}` : `${base}${articlePath}`;
1867
+ return {
1868
+ title: applyTitleTemplate(config, titleText),
1869
+ siteName: config.siteName,
1870
+ twitter: config.twitter ?? "",
1871
+ description,
1872
+ url,
1873
+ keywords: config.defaultKeywords ?? (categoryName ? `${categoryName}, ${config.siteName}` : `${config.siteName} ${label.toLowerCase()}`),
1874
+ image: buildOgImageUrl(config, {
1875
+ title: titleText,
1876
+ subtitle: description,
1877
+ eyebrow: categoryName ? "Category" : label,
1878
+ type: "listing"
1879
+ }),
1880
+ noindex: options?.noindex ?? config.noindex,
1881
+ manifestUrl: config.manifestUrl
1882
+ };
1883
+ }
1884
+ function seoValuesToClientProps(values) {
1885
+ return {
1886
+ title: values.title,
1887
+ description: values.description,
1888
+ url: values.url,
1889
+ siteName: values.siteName,
1890
+ keywords: values.keywords,
1891
+ twitter: values.twitter,
1892
+ image: values.image,
1893
+ noindex: values.noindex
1894
+ };
1895
+ }
1896
+
1897
+ // src/seo/jsonld.ts
1898
+ function buildArticleJsonLd(props) {
1899
+ return {
1900
+ "@context": "https://schema.org",
1901
+ "@type": props.articleType ?? "Article",
1902
+ headline: props.title,
1903
+ description: props.description,
1904
+ url: props.url,
1905
+ ...props.image ? { image: props.image } : {},
1906
+ ...props.publishedTime ? { datePublished: props.publishedTime } : {},
1907
+ ...props.category ? { articleSection: props.category } : {},
1908
+ ...props.tags && props.tags.length > 0 ? { keywords: props.tags.join(", ") } : {},
1909
+ author: { "@type": "Person", name: props.authorName || props.siteName },
1910
+ publisher: { "@id": `${props.siteUrl}/#organization` },
1911
+ isPartOf: { "@id": `${props.siteUrl}/#website` },
1912
+ inLanguage: "en-US"
1913
+ };
1914
+ }
1915
+ function buildCollectionJsonLd(props) {
1916
+ return {
1917
+ "@context": "https://schema.org",
1918
+ "@type": "CollectionPage",
1919
+ name: props.name,
1920
+ description: props.description,
1921
+ url: props.url,
1922
+ isPartOf: { "@id": `${props.siteUrl}/#website` },
1923
+ publisher: { "@id": `${props.siteUrl}/#organization` },
1924
+ inLanguage: "en-US"
1925
+ };
1926
+ }
1927
+ function renderArticleBody(args) {
1928
+ const { post, cmsImage: cmsImage2, relatedPosts, seoNode, jsonLdNode, renderProps: r } = args;
1929
+ const slot = { post, cmsImage: cmsImage2 };
1930
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1931
+ seoNode,
1932
+ jsonLdNode,
1933
+ r.backLink,
1934
+ r.renderPreArticle?.(slot),
1935
+ r.renderHeader?.(slot),
1936
+ r.renderMeta?.(slot),
1937
+ r.renderDescription?.(slot),
1938
+ r.renderFeaturedImage?.(slot),
1939
+ r.renderToc?.(slot),
1940
+ r.renderContent?.(slot),
1941
+ r.renderMidArticle?.(slot),
1942
+ r.renderTags?.(slot),
1943
+ r.renderAuthorDetails?.(slot),
1944
+ r.renderRelatedPosts?.({ post, relatedPosts, cmsImage: cmsImage2 }),
1945
+ r.renderCTA?.(slot),
1946
+ r.renderPostArticle?.(slot)
1947
+ ] });
1948
+ }
1949
+ function AsteroidArticlePage(props) {
1950
+ const {
1951
+ slug,
1952
+ useArticle,
1953
+ seo,
1954
+ articleType,
1955
+ noindex,
1956
+ relatedPosts = [],
1957
+ renderRoot,
1958
+ renderSkeleton,
1959
+ renderError,
1960
+ renderJsonLd,
1961
+ children,
1962
+ ...bodyRenderProps
1963
+ } = props;
1964
+ const { data: article, loading, error } = useArticle(slug);
1965
+ const cmsConfig = react.useContext(AsteroidCMSContext);
1966
+ const cmsImage2 = useCmsImage();
1967
+ const seoConfig = seo && !seo.cmsUrl && cmsConfig?.cmsUrl ? { ...seo, cmsUrl: cmsConfig.cmsUrl } : seo;
1968
+ if (children) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children({ data: article, loading, error }) });
1969
+ if (loading) {
1970
+ const body2 = renderSkeleton?.() ?? null;
1971
+ return renderRoot ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderRoot({ children: body2 }) }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: body2 });
1972
+ }
1973
+ if (!article || error) {
1974
+ const body2 = renderError?.({ error, reason: error ? "error" : "not-found" }) ?? null;
1975
+ return renderRoot ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderRoot({ children: body2 }) }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: body2 });
1976
+ }
1977
+ const seoValues = seoConfig ? buildArticleSeoValues(article, seoConfig, slug, { noindex }) : null;
1978
+ const seoNode = seoValues ? /* @__PURE__ */ jsxRuntime.jsx(Seo, { ...seoValuesToClientProps(seoValues) }) : null;
1979
+ const jsonLdNode = seoConfig ? renderJsonLd?.({ post: article }) ?? /* @__PURE__ */ jsxRuntime.jsx(
1980
+ JsonLd,
1981
+ {
1982
+ data: buildArticleJsonLd({
1983
+ title: article.title,
1984
+ description: article.description || seoConfig.defaultDescription || "",
1985
+ url: `${(seoConfig.baseUrl || "").replace(/\/$/, "")}${seoConfig.articlePath ?? "/blog"}/${slug}`,
1986
+ siteName: seoConfig.siteName,
1987
+ siteUrl: (seoConfig.baseUrl || "").replace(/\/$/, ""),
1988
+ articleType,
1989
+ image: seoValues?.image,
1990
+ authorName: article.author?.name,
1991
+ publishedTime: article.published_date || void 0,
1992
+ tags: article.tags?.split(",").map((t) => t.trim()).filter(Boolean),
1993
+ category: article.category?.name
1994
+ })
1995
+ }
1996
+ ) : null;
1997
+ const body = renderArticleBody({
1998
+ post: article,
1999
+ cmsImage: cmsImage2,
2000
+ relatedPosts,
2001
+ seoNode,
2002
+ jsonLdNode,
2003
+ renderProps: bodyRenderProps
2004
+ });
2005
+ return renderRoot ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderRoot({ children: body }) }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: body });
2006
+ }
2007
+
2008
+ // src/components/articles/articles.state.ts
2009
+ function defaultGetCategoryName(post) {
2010
+ return post.category?.name?.trim() || void 0;
2011
+ }
2012
+ function defaultGroupPostsByCategory(posts) {
2013
+ const groups = /* @__PURE__ */ new Map();
2014
+ for (const post of posts) {
2015
+ const categoryName = defaultGetCategoryName(post) || "Other";
2016
+ const categorySlug = post.category?.slug || "other";
2017
+ const existing = groups.get(categorySlug);
2018
+ if (existing) {
2019
+ existing.posts.push(post);
2020
+ } else {
2021
+ groups.set(categorySlug, { categoryName, categorySlug, posts: [post] });
2022
+ }
2023
+ }
2024
+ return Array.from(groups.values());
2025
+ }
2026
+ function applyPostFilters(posts, { categorySlug, articleSlug }) {
2027
+ let filtered = posts;
2028
+ if (categorySlug) {
2029
+ filtered = filtered.filter((post) => post.category?.slug === categorySlug);
2030
+ }
2031
+ if (articleSlug) {
2032
+ filtered = filtered.filter((post) => post.slug === articleSlug);
2033
+ }
2034
+ return filtered;
2035
+ }
2036
+ function splitFeaturedAndRest(posts) {
2037
+ const featured = posts.find((post) => post.featured) ?? null;
2038
+ const rest = featured ? posts.filter((post) => post.slug !== featured.slug) : [...posts];
2039
+ return { featured, rest };
2040
+ }
2041
+ function buildArticlesViewState(rawPosts, options) {
2042
+ const {
2043
+ categorySlug,
2044
+ articleSlug,
2045
+ searchQuery = "",
2046
+ groupPostsByCategory = defaultGroupPostsByCategory
2047
+ } = options;
2048
+ const posts = applyPostFilters(rawPosts, { categorySlug, articleSlug });
2049
+ const { featured, rest } = splitFeaturedAndRest(posts);
2050
+ const trimmedQuery = searchQuery.trim();
2051
+ const isSearching = trimmedQuery.length > 0;
2052
+ const categoryGroups = isSearching ? posts.length === 0 ? [] : [
2053
+ {
2054
+ categoryName: `Search results for "${trimmedQuery}"`,
2055
+ categorySlug: "search-results",
2056
+ posts
2057
+ }
2058
+ ] : groupPostsByCategory(rest);
2059
+ return {
2060
+ posts,
2061
+ featured,
2062
+ rest,
2063
+ categoryGroups,
2064
+ isEmpty: !featured && rest.length === 0,
2065
+ isSearching,
2066
+ searchQuery: trimmedQuery
2067
+ };
2068
+ }
2069
+ function renderArticlesListingBody(args) {
2070
+ const { state, loading, hasError, error, cmsImage: cmsImage2, searchNode, seoNode, jsonLdNode, renderProps } = args;
2071
+ const {
2072
+ eyebrow,
2073
+ title,
2074
+ description,
2075
+ renderRoot,
2076
+ renderHeader,
2077
+ renderFeaturedCard,
2078
+ renderPostCard,
2079
+ renderCategoryHeading,
2080
+ renderPostGrid,
2081
+ renderCategoryGroup,
2082
+ renderSkeleton,
2083
+ renderEmpty,
2084
+ renderContent
2085
+ } = renderProps;
2086
+ const headerNode = renderHeader ? renderHeader({ eyebrow, title, description, search: searchNode }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2087
+ eyebrow,
2088
+ title,
2089
+ description,
2090
+ searchNode
2091
+ ] });
2092
+ const featuredNode = state.featured && !state.isSearching && renderFeaturedCard ? renderFeaturedCard({ post: state.featured, cmsImage: cmsImage2 }) : null;
2093
+ const noSearchResultsNode = state.isSearching && !loading && state.posts.length === 0 ? renderEmpty?.({ reason: "no-results", searchQuery: state.searchQuery }) ?? null : null;
2094
+ const groupsNode = noSearchResultsNode ? null : state.categoryGroups.map((group) => {
2095
+ const postCards = group.posts.map((post, index) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderPostCard({ post, index, group, cmsImage: cmsImage2 }) }, post.slug ?? index));
2096
+ const gridNode = renderPostGrid ? renderPostGrid({ posts: group.posts, group, children: postCards }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: postCards });
2097
+ const headingNode = renderCategoryHeading ? renderCategoryHeading({ group }) : group.categoryName;
2098
+ const defaultContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2099
+ headingNode,
2100
+ gridNode
2101
+ ] });
2102
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderCategoryGroup ? renderCategoryGroup({ group, defaultContent }) : defaultContent }, group.categorySlug);
2103
+ });
2104
+ const contentNode = !loading && !hasError && (state.featured || state.rest.length > 0) ? renderContent ? renderContent({ featured: featuredNode, groups: groupsNode, noSearchResults: noSearchResultsNode }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2105
+ featuredNode,
2106
+ noSearchResultsNode,
2107
+ groupsNode
2108
+ ] }) : null;
2109
+ const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2110
+ seoNode,
2111
+ jsonLdNode,
2112
+ headerNode,
2113
+ loading ? renderSkeleton?.() : null,
2114
+ !loading && (hasError || state.isEmpty) ? renderEmpty?.({
2115
+ reason: hasError ? "error" : state.isSearching ? "no-results" : "no-posts",
2116
+ searchQuery: state.searchQuery,
2117
+ error
2118
+ }) : null,
2119
+ contentNode
2120
+ ] });
2121
+ return renderRoot ? renderRoot({ children: body }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: body });
2122
+ }
2123
+ function useDebouncedValue(value, delay) {
2124
+ const [debounced, setDebounced] = react.useState(value);
2125
+ react.useEffect(() => {
2126
+ const timer = setTimeout(() => setDebounced(value), delay);
2127
+ return () => clearTimeout(timer);
2128
+ }, [value, delay]);
2129
+ return debounced;
2130
+ }
2131
+ function useAsteroidArticlesState(props) {
2132
+ const { usePosts, categorySlug, articleSlug, searchDebounceMs = 800, groupPostsByCategory } = props;
2133
+ const [inputValue, setSearchQuery] = react.useState("");
2134
+ const debouncedSearchQuery = useDebouncedValue(inputValue, searchDebounceMs);
2135
+ const { posts: rawPosts, loading, error } = usePosts(debouncedSearchQuery);
2136
+ const view = react.useMemo(
2137
+ () => buildArticlesViewState(rawPosts, {
2138
+ categorySlug,
2139
+ articleSlug,
2140
+ searchQuery: debouncedSearchQuery,
2141
+ groupPostsByCategory
2142
+ }),
2143
+ [rawPosts, categorySlug, articleSlug, debouncedSearchQuery, groupPostsByCategory]
2144
+ );
2145
+ return {
2146
+ ...view,
2147
+ loading,
2148
+ error,
2149
+ hasError: Boolean(error),
2150
+ inputValue,
2151
+ setSearchQuery
2152
+ };
2153
+ }
2154
+ function AsteroidArticlesListing(props) {
2155
+ const { seo, categorySlug, noindex, renderSearch, renderJsonLd, children, ...renderProps } = props;
2156
+ const state = useAsteroidArticlesState(props);
2157
+ const cmsImage2 = useCmsImage();
2158
+ if (children) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children(state) });
2159
+ const categoryName = categorySlug ? state.posts[0]?.category?.name?.trim() : void 0;
2160
+ const seoNode = seo ? /* @__PURE__ */ jsxRuntime.jsx(
2161
+ Seo,
2162
+ {
2163
+ ...seoValuesToClientProps(
2164
+ buildArticleListingSeoValues(seo, { categoryName, categorySlug, noindex })
2165
+ )
2166
+ }
2167
+ ) : null;
2168
+ const jsonLdNode = seo ? renderJsonLd?.(state) ?? /* @__PURE__ */ jsxRuntime.jsx(
2169
+ JsonLd,
2170
+ {
2171
+ data: buildCollectionJsonLd({
2172
+ name: categoryName || `${seo.siteName} ${seo.contentLabel ?? "Articles"}`,
2173
+ description: seo.defaultDescription || "",
2174
+ url: `${(seo.baseUrl || "").replace(/\/$/, "")}${seo.articlePath ?? "/blog"}${categorySlug ? `/category/${categorySlug}` : ""}`,
2175
+ siteUrl: (seo.baseUrl || "").replace(/\/$/, "")
2176
+ })
2177
+ }
2178
+ ) : null;
2179
+ const searchNode = renderSearch ? renderSearch({
2180
+ value: state.inputValue,
2181
+ onChange: state.setSearchQuery,
2182
+ onSubmit: (event) => event.preventDefault()
2183
+ }) : null;
2184
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderArticlesListingBody({
2185
+ state,
2186
+ loading: state.loading,
2187
+ hasError: state.hasError,
2188
+ error: state.error,
2189
+ cmsImage: cmsImage2,
2190
+ searchNode,
2191
+ seoNode,
2192
+ jsonLdNode,
2193
+ renderProps
2194
+ }) });
2195
+ }
2196
+ function ArticleSearchBox({
2197
+ paramKey = "q",
2198
+ placeholder = "Search articles...",
2199
+ debounceMs = 500,
2200
+ className,
2201
+ render
2202
+ }) {
2203
+ const router = navigation.useRouter();
2204
+ const pathname = navigation.usePathname();
2205
+ const searchParams = navigation.useSearchParams();
2206
+ const initial = searchParams.get(paramKey) ?? "";
2207
+ const [value, setValue] = react.useState(initial);
2208
+ const searchParamsRef = react.useRef(searchParams);
2209
+ searchParamsRef.current = searchParams;
2210
+ react.useEffect(() => {
2211
+ const timer = setTimeout(() => {
2212
+ const params = new URLSearchParams(Array.from(searchParamsRef.current.entries()));
2213
+ const trimmed = value.trim();
2214
+ if (trimmed) params.set(paramKey, trimmed);
2215
+ else params.delete(paramKey);
2216
+ const query = params.toString();
2217
+ router.replace(query ? `${pathname}?${query}` : pathname, { scroll: false });
2218
+ }, debounceMs);
2219
+ return () => clearTimeout(timer);
2220
+ }, [value, debounceMs, paramKey, pathname]);
2221
+ const onSubmit = (event) => event.preventDefault();
2222
+ if (render) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: render({ value, onChange: setValue, onSubmit }) });
2223
+ return /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit, className, children: /* @__PURE__ */ jsxRuntime.jsx(
2224
+ "input",
2225
+ {
2226
+ type: "search",
2227
+ value,
2228
+ placeholder,
2229
+ "aria-label": placeholder,
2230
+ onChange: (event) => setValue(event.target.value)
2231
+ }
2232
+ ) });
2233
+ }
1737
2234
 
2235
+ exports.ArticleSearchBox = ArticleSearchBox;
2236
+ exports.AsteroidArticlePage = AsteroidArticlePage;
2237
+ exports.AsteroidArticlesListing = AsteroidArticlesListing;
1738
2238
  exports.AsteroidCMSProvider = AsteroidCMSProvider;
2239
+ exports.JsonLd = JsonLd;
1739
2240
  exports.RichTextContent = RichTextContent;
2241
+ exports.Seo = Seo;
2242
+ exports.defaultGetCategoryName = defaultGetCategoryName;
2243
+ exports.defaultGroupPostsByCategory = defaultGroupPostsByCategory;
1740
2244
  exports.extractHeadingsFromElement = extractHeadingsFromElement;
1741
2245
  exports.extractHeadingsFromHtml = extractHeadingsFromHtml;
1742
2246
  exports.slugify = slugify;
2247
+ exports.useAsteroidArticlesState = useAsteroidArticlesState;
1743
2248
  exports.useAsteroidCMSConfig = useAsteroidCMSConfig;
1744
2249
  exports.useCmsContent = useCmsContent;
1745
2250
  exports.useCmsImage = useCmsImage;