@cedros/data-react 0.1.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.
Files changed (152) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +177 -0
  3. package/dist/admin/api.d.ts +19 -0
  4. package/dist/admin/api.js +108 -0
  5. package/dist/admin/components.d.ts +36 -0
  6. package/dist/admin/components.js +22 -0
  7. package/dist/admin/history.d.ts +17 -0
  8. package/dist/admin/history.js +103 -0
  9. package/dist/admin/icons.d.ts +15 -0
  10. package/dist/admin/icons.js +18 -0
  11. package/dist/admin/index.d.ts +13 -0
  12. package/dist/admin/index.js +12 -0
  13. package/dist/admin/permissions.d.ts +4 -0
  14. package/dist/admin/permissions.js +45 -0
  15. package/dist/admin/plugin.d.ts +4 -0
  16. package/dist/admin/plugin.js +180 -0
  17. package/dist/admin/primitives/ConfirmDialog.d.ts +14 -0
  18. package/dist/admin/primitives/ConfirmDialog.js +7 -0
  19. package/dist/admin/primitives/DataTable.d.ts +14 -0
  20. package/dist/admin/primitives/DataTable.js +7 -0
  21. package/dist/admin/primitives/DiffViewer.d.ts +11 -0
  22. package/dist/admin/primitives/DiffViewer.js +8 -0
  23. package/dist/admin/primitives/FormFieldRow.d.ts +23 -0
  24. package/dist/admin/primitives/FormFieldRow.js +16 -0
  25. package/dist/admin/primitives/JsonCodeEditor.d.ts +10 -0
  26. package/dist/admin/primitives/JsonCodeEditor.js +42 -0
  27. package/dist/admin/primitives/Pagination.d.ts +8 -0
  28. package/dist/admin/primitives/Pagination.js +8 -0
  29. package/dist/admin/primitives/Toolbar.d.ts +23 -0
  30. package/dist/admin/primitives/Toolbar.js +10 -0
  31. package/dist/admin/primitives/alerts.d.ts +21 -0
  32. package/dist/admin/primitives/alerts.js +44 -0
  33. package/dist/admin/sectionIds.d.ts +20 -0
  34. package/dist/admin/sectionIds.js +33 -0
  35. package/dist/admin/sections/CollectionsSection.d.ts +2 -0
  36. package/dist/admin/sections/CollectionsSection.js +125 -0
  37. package/dist/admin/sections/ContractVerifySection.d.ts +11 -0
  38. package/dist/admin/sections/ContractVerifySection.js +98 -0
  39. package/dist/admin/sections/CustomDataSection.d.ts +2 -0
  40. package/dist/admin/sections/CustomDataSection.js +256 -0
  41. package/dist/admin/sections/DataOpsSection.d.ts +26 -0
  42. package/dist/admin/sections/DataOpsSection.js +245 -0
  43. package/dist/admin/sections/HistorySection.d.ts +2 -0
  44. package/dist/admin/sections/HistorySection.js +26 -0
  45. package/dist/admin/sections/MonetizationSection.d.ts +2 -0
  46. package/dist/admin/sections/MonetizationSection.js +140 -0
  47. package/dist/admin/sections/NavigationSection.d.ts +13 -0
  48. package/dist/admin/sections/NavigationSection.js +195 -0
  49. package/dist/admin/sections/PagesSection.d.ts +2 -0
  50. package/dist/admin/sections/PagesSection.js +157 -0
  51. package/dist/admin/sections/SchemaDesignerSection.d.ts +2 -0
  52. package/dist/admin/sections/SchemaDesignerSection.js +167 -0
  53. package/dist/admin/sections/SiteSettingsSection.d.ts +12 -0
  54. package/dist/admin/sections/SiteSettingsSection.js +122 -0
  55. package/dist/admin/sections/TippingSection.d.ts +2 -0
  56. package/dist/admin/sections/TippingSection.js +178 -0
  57. package/dist/admin/sections/media/MediaDetail.d.ts +12 -0
  58. package/dist/admin/sections/media/MediaDetail.js +74 -0
  59. package/dist/admin/sections/media/MediaGrid.d.ts +14 -0
  60. package/dist/admin/sections/media/MediaGrid.js +22 -0
  61. package/dist/admin/sections/media/MediaSection.d.ts +2 -0
  62. package/dist/admin/sections/media/MediaSection.js +97 -0
  63. package/dist/admin/sections/media/MediaUploader.d.ts +7 -0
  64. package/dist/admin/sections/media/MediaUploader.js +72 -0
  65. package/dist/admin/sections/media/types.d.ts +33 -0
  66. package/dist/admin/sections/media/types.js +1 -0
  67. package/dist/admin/styles.css +533 -0
  68. package/dist/admin/types.d.ts +85 -0
  69. package/dist/admin/types.js +1 -0
  70. package/dist/index.d.ts +4 -0
  71. package/dist/index.js +3 -0
  72. package/dist/react/CmsContent.d.ts +20 -0
  73. package/dist/react/CmsContent.js +31 -0
  74. package/dist/react/entries.d.ts +9 -0
  75. package/dist/react/entries.js +25 -0
  76. package/dist/react/fetch.d.ts +11 -0
  77. package/dist/react/fetch.js +32 -0
  78. package/dist/react/index.d.ts +10 -0
  79. package/dist/react/index.js +9 -0
  80. package/dist/react/metadata.d.ts +44 -0
  81. package/dist/react/metadata.js +142 -0
  82. package/dist/react/sanitize.d.ts +17 -0
  83. package/dist/react/sanitize.js +326 -0
  84. package/dist/react/server.d.ts +14 -0
  85. package/dist/react/server.js +13 -0
  86. package/dist/react/sitemap.d.ts +28 -0
  87. package/dist/react/sitemap.js +91 -0
  88. package/dist/react/slugs.d.ts +27 -0
  89. package/dist/react/slugs.js +52 -0
  90. package/dist/react/types.d.ts +85 -0
  91. package/dist/react/types.js +1 -0
  92. package/dist/react/visitor.d.ts +7 -0
  93. package/dist/react/visitor.js +18 -0
  94. package/dist/site-templates/BlogTemplates.d.ts +95 -0
  95. package/dist/site-templates/BlogTemplates.js +64 -0
  96. package/dist/site-templates/ContactPageTemplate.d.ts +14 -0
  97. package/dist/site-templates/ContactPageTemplate.js +5 -0
  98. package/dist/site-templates/DashboardOverviewTemplate.d.ts +29 -0
  99. package/dist/site-templates/DashboardOverviewTemplate.js +17 -0
  100. package/dist/site-templates/DashboardShell.d.ts +28 -0
  101. package/dist/site-templates/DashboardShell.js +10 -0
  102. package/dist/site-templates/DocsSidebar.d.ts +14 -0
  103. package/dist/site-templates/DocsSidebar.js +13 -0
  104. package/dist/site-templates/DocsTemplates.d.ts +60 -0
  105. package/dist/site-templates/DocsTemplates.js +47 -0
  106. package/dist/site-templates/HomePageTemplate.d.ts +15 -0
  107. package/dist/site-templates/HomePageTemplate.js +10 -0
  108. package/dist/site-templates/LegalPageTemplate.d.ts +12 -0
  109. package/dist/site-templates/LegalPageTemplate.js +6 -0
  110. package/dist/site-templates/MarkdownContent.d.ts +7 -0
  111. package/dist/site-templates/MarkdownContent.js +24 -0
  112. package/dist/site-templates/NotFoundTemplate.d.ts +9 -0
  113. package/dist/site-templates/NotFoundTemplate.js +5 -0
  114. package/dist/site-templates/SiteFooter.d.ts +13 -0
  115. package/dist/site-templates/SiteFooter.js +4 -0
  116. package/dist/site-templates/SiteLayout.d.ts +14 -0
  117. package/dist/site-templates/SiteLayout.js +6 -0
  118. package/dist/site-templates/TopNav.d.ts +10 -0
  119. package/dist/site-templates/TopNav.js +8 -0
  120. package/dist/site-templates/blogControls.d.ts +19 -0
  121. package/dist/site-templates/blogControls.js +37 -0
  122. package/dist/site-templates/codeBlock.d.ts +9 -0
  123. package/dist/site-templates/codeBlock.js +31 -0
  124. package/dist/site-templates/content-styles.css +410 -0
  125. package/dist/site-templates/contentIndex.d.ts +65 -0
  126. package/dist/site-templates/contentIndex.js +181 -0
  127. package/dist/site-templates/contentUi.d.ts +14 -0
  128. package/dist/site-templates/contentUi.js +24 -0
  129. package/dist/site-templates/docs-styles.css +259 -0
  130. package/dist/site-templates/docsNavigation.d.ts +18 -0
  131. package/dist/site-templates/docsNavigation.js +50 -0
  132. package/dist/site-templates/index.d.ts +28 -0
  133. package/dist/site-templates/index.js +25 -0
  134. package/dist/site-templates/monetization-styles.css +154 -0
  135. package/dist/site-templates/paywallControls.d.ts +22 -0
  136. package/dist/site-templates/paywallControls.js +9 -0
  137. package/dist/site-templates/routing.d.ts +12 -0
  138. package/dist/site-templates/routing.js +36 -0
  139. package/dist/site-templates/solanaAtaSetup.d.ts +11 -0
  140. package/dist/site-templates/solanaAtaSetup.js +38 -0
  141. package/dist/site-templates/solanaMicropayments.d.ts +65 -0
  142. package/dist/site-templates/solanaMicropayments.js +115 -0
  143. package/dist/site-templates/styles.css +332 -0
  144. package/dist/site-templates/tipControls.d.ts +24 -0
  145. package/dist/site-templates/tipControls.js +43 -0
  146. package/dist/site-templates/tocExtractor.d.ts +16 -0
  147. package/dist/site-templates/tocExtractor.js +58 -0
  148. package/dist/site-templates/tocScrollSpy.d.ts +16 -0
  149. package/dist/site-templates/tocScrollSpy.js +37 -0
  150. package/dist/templates.d.ts +8 -0
  151. package/dist/templates.js +20 -0
  152. package/package.json +58 -0
@@ -0,0 +1,91 @@
1
+ import { fetchJson, resolveServerUrl } from "./fetch.js";
2
+ /** Map of content types to their collection names in cedros-data. */
3
+ const CONTENT_TYPE_COLLECTIONS = {
4
+ page: "pages",
5
+ blog: "blog",
6
+ docs: "docs",
7
+ learn: "learn",
8
+ project: "projects",
9
+ airdrop: "airdrops",
10
+ };
11
+ /** Default change frequency and priority for each content type. */
12
+ const CONTENT_TYPE_DEFAULTS = {
13
+ page: { changeFrequency: "monthly", priority: 0.8 },
14
+ blog: { changeFrequency: "weekly", priority: 0.7 },
15
+ docs: { changeFrequency: "weekly", priority: 0.6 },
16
+ learn: { changeFrequency: "monthly", priority: 0.5 },
17
+ project: { changeFrequency: "monthly", priority: 0.5 },
18
+ airdrop: { changeFrequency: "daily", priority: 0.4 },
19
+ };
20
+ const ALL_CONTENT_TYPES = [
21
+ "page",
22
+ "blog",
23
+ "docs",
24
+ "learn",
25
+ "project",
26
+ "airdrop",
27
+ ];
28
+ /**
29
+ * Loads all routable content slugs from cedros-data for sitemap generation.
30
+ *
31
+ * Queries each content type collection and returns a flat list of sitemap
32
+ * entries with slug, lastModified, changeFrequency, and priority.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * // app/sitemap.ts
37
+ * import { loadSitemapEntries } from '@cedros/data-react/server';
38
+ *
39
+ * export default async function sitemap() {
40
+ * const entries = await loadSitemapEntries({
41
+ * serverUrl: process.env.NEXT_PUBLIC_BACKEND_API_URL,
42
+ * });
43
+ * return entries.map((e) => ({
44
+ * url: `${process.env.NEXT_PUBLIC_ORIGIN}/${e.slug}`,
45
+ * lastModified: e.lastModified,
46
+ * changeFrequency: e.changeFrequency,
47
+ * priority: e.priority,
48
+ * }));
49
+ * }
50
+ * ```
51
+ */
52
+ export async function loadSitemapEntries(options) {
53
+ const serverUrl = resolveServerUrl(options);
54
+ const contentTypes = options?.contentTypes ?? ALL_CONTENT_TYPES;
55
+ const results = await Promise.allSettled(contentTypes.map((ct) => fetchCollectionSlugs(serverUrl, ct)));
56
+ const entries = [];
57
+ for (let i = 0; i < results.length; i++) {
58
+ const result = results[i];
59
+ if (result.status === "fulfilled") {
60
+ entries.push(...result.value);
61
+ }
62
+ else {
63
+ console.warn(`@cedros/data-react: skipping sitemap content type "${contentTypes[i]}" — ${result.reason}`);
64
+ }
65
+ }
66
+ return entries;
67
+ }
68
+ async function fetchCollectionSlugs(serverUrl, contentType) {
69
+ const collectionName = CONTENT_TYPE_COLLECTIONS[contentType];
70
+ const defaults = CONTENT_TYPE_DEFAULTS[contentType];
71
+ const records = await fetchJson(serverUrl, "/entries/query", {
72
+ method: "POST",
73
+ body: {
74
+ collection_name: collectionName,
75
+ limit: 1000,
76
+ offset: 0,
77
+ },
78
+ });
79
+ return records.map((record) => {
80
+ const slug = record.payload.slug ??
81
+ record.payload.route?.replace(/^\//, "") ??
82
+ record.entry_key;
83
+ return {
84
+ slug,
85
+ contentType,
86
+ lastModified: record.updated_at,
87
+ changeFrequency: defaults.changeFrequency,
88
+ priority: defaults.priority,
89
+ };
90
+ });
91
+ }
@@ -0,0 +1,27 @@
1
+ import type { ContentType, ServerFetchOptions } from "./types.js";
2
+ /**
3
+ * Returns all slugs for a given content type, for use with
4
+ * Next.js `generateStaticParams`.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * // app/blog/[slug]/page.tsx
9
+ * import { listContentSlugs } from '@cedros/data-react/server';
10
+ *
11
+ * export async function generateStaticParams() {
12
+ * const slugs = await listContentSlugs('blog');
13
+ * return slugs.map((slug) => ({ slug }));
14
+ * }
15
+ * ```
16
+ */
17
+ export declare function listContentSlugs(contentType: ContentType, options?: ServerFetchOptions): Promise<string[]>;
18
+ /**
19
+ * Returns all blog post slugs for `generateStaticParams`.
20
+ * Convenience wrapper around `listContentSlugs('blog')`.
21
+ */
22
+ export declare function listBlogSlugs(options?: ServerFetchOptions): Promise<string[]>;
23
+ /**
24
+ * Returns all learn path IDs for `generateStaticParams`.
25
+ * Convenience wrapper around `listContentSlugs('learn')`.
26
+ */
27
+ export declare function listLearnPathIds(options?: ServerFetchOptions): Promise<string[]>;
@@ -0,0 +1,52 @@
1
+ import { fetchJson, resolveServerUrl } from "./fetch.js";
2
+ /** Map of content types to their collection names in cedros-data. */
3
+ const CONTENT_TYPE_COLLECTIONS = {
4
+ page: "pages",
5
+ blog: "blog",
6
+ docs: "docs",
7
+ learn: "learn",
8
+ project: "projects",
9
+ airdrop: "airdrops",
10
+ };
11
+ /**
12
+ * Returns all slugs for a given content type, for use with
13
+ * Next.js `generateStaticParams`.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // app/blog/[slug]/page.tsx
18
+ * import { listContentSlugs } from '@cedros/data-react/server';
19
+ *
20
+ * export async function generateStaticParams() {
21
+ * const slugs = await listContentSlugs('blog');
22
+ * return slugs.map((slug) => ({ slug }));
23
+ * }
24
+ * ```
25
+ */
26
+ export async function listContentSlugs(contentType, options) {
27
+ const serverUrl = resolveServerUrl(options);
28
+ const collectionName = CONTENT_TYPE_COLLECTIONS[contentType];
29
+ const records = await fetchJson(serverUrl, "/entries/query", {
30
+ method: "POST",
31
+ body: {
32
+ collection_name: collectionName,
33
+ limit: 1000,
34
+ offset: 0,
35
+ },
36
+ });
37
+ return records.map((record) => record.payload.slug ?? record.entry_key);
38
+ }
39
+ /**
40
+ * Returns all blog post slugs for `generateStaticParams`.
41
+ * Convenience wrapper around `listContentSlugs('blog')`.
42
+ */
43
+ export async function listBlogSlugs(options) {
44
+ return listContentSlugs("blog", options);
45
+ }
46
+ /**
47
+ * Returns all learn path IDs for `generateStaticParams`.
48
+ * Convenience wrapper around `listContentSlugs('learn')`.
49
+ */
50
+ export async function listLearnPathIds(options) {
51
+ return listContentSlugs("learn", options);
52
+ }
@@ -0,0 +1,85 @@
1
+ /** Content type identifiers used across cedros-data. */
2
+ export type ContentType = "page" | "blog" | "docs" | "learn" | "project" | "airdrop";
3
+ /**
4
+ * A CMS page record as returned by the cedros-data entries API.
5
+ * The payload shape follows the default page schema seeded by bootstrap.
6
+ */
7
+ export interface CmsPageRecord {
8
+ entry_key: string;
9
+ payload: {
10
+ key?: string;
11
+ title?: string;
12
+ description?: string;
13
+ route?: string;
14
+ section?: string;
15
+ status?: string;
16
+ body?: string;
17
+ bodyHtml?: string;
18
+ ogTitle?: string;
19
+ ogDescription?: string;
20
+ ogImage?: string;
21
+ twitterTitle?: string;
22
+ twitterDescription?: string;
23
+ author?: string;
24
+ publishedAt?: string;
25
+ updatedAt?: string;
26
+ category?: string;
27
+ tags?: string[];
28
+ slug?: string;
29
+ readingMinutes?: number;
30
+ wordCount?: number;
31
+ _metering?: MeteredReadsInfo;
32
+ [key: string]: unknown;
33
+ };
34
+ updated_at: string;
35
+ }
36
+ /** Site-level settings record from the site_settings collection. */
37
+ export interface SiteDataRecord {
38
+ siteTitle?: string;
39
+ siteName?: string;
40
+ siteDescription?: string;
41
+ defaultDescription?: string;
42
+ defaultOgImage?: string;
43
+ locale?: string;
44
+ timezone?: string;
45
+ [key: string]: unknown;
46
+ }
47
+ /** A routable content entry for sitemap generation. */
48
+ export interface SitemapEntry {
49
+ slug: string;
50
+ contentType: ContentType;
51
+ lastModified?: string;
52
+ changeFrequency?: "daily" | "weekly" | "monthly";
53
+ priority?: number;
54
+ }
55
+ /** Options for the HTML sanitizer. */
56
+ export interface SanitizeOptions {
57
+ /** Tags to allow. Defaults to a safe prose subset. */
58
+ allowedTags?: string[];
59
+ /** Attributes to allow per tag. Defaults to a safe set. */
60
+ allowedAttributes?: Record<string, string[]>;
61
+ /** Default target for links. */
62
+ linkTarget?: string;
63
+ /** Allowed URL protocols for href/src attributes. */
64
+ allowedProtocols?: string[];
65
+ }
66
+ /** Options for building page metadata. */
67
+ export interface BuildPageMetadataOptions {
68
+ siteData?: SiteDataRecord;
69
+ origin?: string;
70
+ path?: string;
71
+ mode?: "blog" | "docs" | "page";
72
+ }
73
+ /** Metering status injected into entry payloads when visitor_id is provided. */
74
+ export interface MeteredReadsInfo {
75
+ remainingFreeReads: number;
76
+ freeReadsPerMonth: number;
77
+ readsUsed: number;
78
+ }
79
+ /** Options for server-side data fetching helpers. */
80
+ export interface ServerFetchOptions {
81
+ /** Base URL of the cedros-data server. Falls back to CEDROS_DATA_URL env var. */
82
+ serverUrl?: string;
83
+ /** Visitor ID for metered-reads tracking. Use `getOrCreateVisitorId()` on the client. */
84
+ visitorId?: string;
85
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Client-side visitor identification via cookie.
3
+ *
4
+ * Returns a stable UUID stored in `cedros_vid` cookie (1-year expiry, SameSite=Lax).
5
+ * Returns `""` during SSR when `document` is unavailable.
6
+ */
7
+ export declare function getOrCreateVisitorId(): string;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Client-side visitor identification via cookie.
3
+ *
4
+ * Returns a stable UUID stored in `cedros_vid` cookie (1-year expiry, SameSite=Lax).
5
+ * Returns `""` during SSR when `document` is unavailable.
6
+ */
7
+ export function getOrCreateVisitorId() {
8
+ if (typeof document === "undefined")
9
+ return "";
10
+ const name = "cedros_vid";
11
+ const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
12
+ if (match)
13
+ return decodeURIComponent(match[1]);
14
+ const id = crypto.randomUUID();
15
+ const maxAge = 365 * 24 * 60 * 60; // 1 year in seconds
16
+ document.cookie = `${name}=${encodeURIComponent(id)}; path=/; max-age=${maxAge}; SameSite=Lax`;
17
+ return id;
18
+ }
@@ -0,0 +1,95 @@
1
+ import type { SiteNavigationItem } from "./SiteLayout.js";
2
+ import { type BlogIndexEntry, type FilterDimension, type FilterDimensionValues } from "./contentIndex.js";
3
+ import { type TipCurrency, type TipPayClient } from "./tipControls.js";
4
+ import { type PaywallPrice, type PaywallPayClient } from "./paywallControls.js";
5
+ export interface BlogPostSummary extends BlogIndexEntry {
6
+ author?: string;
7
+ readingMinutes?: number;
8
+ }
9
+ export interface BlogIndexTemplateProps {
10
+ siteTitle: string;
11
+ navigation: SiteNavigationItem[];
12
+ posts: BlogPostSummary[];
13
+ title?: string;
14
+ description?: string;
15
+ basePath?: string;
16
+ query?: string;
17
+ category?: string;
18
+ tag?: string;
19
+ sort?: "newest" | "oldest" | "title-asc" | "title-desc";
20
+ page?: number;
21
+ pageSize?: number;
22
+ categories?: string[];
23
+ tags?: string[];
24
+ /** Generic filter dimensions (replaces category/tag dropdowns when provided) */
25
+ filterDimensions?: FilterDimension[];
26
+ /** Current active filter values per dimension */
27
+ activeFilters?: FilterDimensionValues;
28
+ /** Called when a dimension filter chip is toggled */
29
+ onFilterChange?: (key: string, value: string) => void;
30
+ /** Called on debounced search input (enables client-side search with CMD+K) */
31
+ onSearch?: (query: string) => void;
32
+ /** Set of bookmarked post slugs */
33
+ bookmarkedSlugs?: Set<string>;
34
+ /** Called when bookmark button is toggled */
35
+ onBookmarkToggle?: (slug: string) => void;
36
+ }
37
+ export declare function BlogIndexTemplate({ siteTitle, navigation, posts, title, description, basePath, query, category, tag, sort, page, pageSize, categories, tags, filterDimensions, activeFilters, onFilterChange, onSearch, bookmarkedSlugs, onBookmarkToggle }: BlogIndexTemplateProps): React.JSX.Element;
38
+ export interface BlogTippingConfig {
39
+ enabled: boolean;
40
+ label?: string;
41
+ description?: string;
42
+ recipient: string;
43
+ currencies: TipCurrency[];
44
+ presets?: Record<string, number[]>;
45
+ payClient: TipPayClient;
46
+ /** When true, `recipientOverride` takes precedence over `recipient`. */
47
+ allowPerPostRecipient?: boolean;
48
+ /** Per-post tip recipient (used when `allowPerPostRecipient` is true). */
49
+ recipientOverride?: string;
50
+ }
51
+ export interface BlogPaywallConfig {
52
+ mode: "free" | "preview" | "locked";
53
+ previewParagraphs?: number;
54
+ price?: PaywallPrice;
55
+ unlocked: boolean;
56
+ payClient: PaywallPayClient;
57
+ subscriptionLabel?: string;
58
+ purchaseLabel?: string;
59
+ /** Remaining metered free reads this month (undefined = metering disabled). */
60
+ remainingFreeReads?: number;
61
+ }
62
+ export interface BlogPostTemplateProps {
63
+ siteTitle: string;
64
+ navigation: SiteNavigationItem[];
65
+ title: string;
66
+ bodyMarkdown?: string;
67
+ bodyHtml?: string;
68
+ allowUnsafeHtmlFallback?: boolean;
69
+ publishedAt?: string;
70
+ updatedAt?: string;
71
+ author?: string;
72
+ category?: string;
73
+ tags?: string[];
74
+ readingMinutes?: number;
75
+ canonicalUrl?: string;
76
+ breadcrumbs?: Array<{
77
+ label: string;
78
+ href: string;
79
+ }>;
80
+ relatedPosts?: Array<{
81
+ slug: string;
82
+ title: string;
83
+ excerpt?: string;
84
+ }>;
85
+ basePath?: string;
86
+ /** Whether this post is bookmarked */
87
+ isBookmarked?: boolean;
88
+ /** Called when bookmark button is toggled */
89
+ onBookmarkToggle?: (slug: string) => void;
90
+ /** Post slug (required when using bookmark) */
91
+ slug?: string;
92
+ tipping?: BlogTippingConfig;
93
+ paywall?: BlogPaywallConfig;
94
+ }
95
+ export declare function BlogPostTemplate({ siteTitle, navigation, title, bodyMarkdown, bodyHtml, allowUnsafeHtmlFallback, publishedAt, updatedAt, author, category, tags, readingMinutes, canonicalUrl, breadcrumbs, relatedPosts, basePath, isBookmarked, onBookmarkToggle, slug, tipping, paywall }: BlogPostTemplateProps): React.JSX.Element;
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { SiteLayout } from "./SiteLayout.js";
3
+ import { buildContentListHref, collectFilterValues, prepareBlogIndex } from "./contentIndex.js";
4
+ import { Breadcrumbs, ContentPagination } from "./contentUi.js";
5
+ import { MarkdownContent } from "./MarkdownContent.js";
6
+ import { BlogSearchInput, BookmarkButton, FilterDimensionChips } from "./blogControls.js";
7
+ import { TipWidget } from "./tipControls.js";
8
+ import { ContentPaywall } from "./paywallControls.js";
9
+ export function BlogIndexTemplate({ siteTitle, navigation, posts, title = "Blog", description = "", basePath = "/blog", query = "", category = "", tag = "", sort = "newest", page = 1, pageSize = 8, categories, tags, filterDimensions, activeFilters, onFilterChange, onSearch, bookmarkedSlugs, onBookmarkToggle }) {
10
+ const normalizedFilters = collectFilterValues(posts);
11
+ const resolvedCategories = categories ?? normalizedFilters.categories;
12
+ const resolvedTags = tags ?? normalizedFilters.tags;
13
+ const result = prepareBlogIndex(posts, {
14
+ query,
15
+ category,
16
+ tag,
17
+ sort,
18
+ page,
19
+ pageSize,
20
+ dimensions: activeFilters
21
+ });
22
+ const useClientControls = !!(filterDimensions || onSearch);
23
+ return (_jsxs(SiteLayout, { siteTitle: siteTitle, navigation: navigation, children: [_jsxs("section", { className: "cedros-site__card", children: [_jsx("h1", { className: "cedros-site__title", children: title }), description && _jsx("p", { className: "cedros-site__subtitle", children: description })] }), useClientControls ? (_jsxs("section", { className: "cedros-site__card", style: { display: "grid", gap: "0.7rem" }, children: [onSearch && _jsx(BlogSearchInput, { defaultValue: query, onSearch: onSearch }), filterDimensions && onFilterChange && (_jsx(FilterDimensionChips, { dimensions: filterDimensions, active: activeFilters ?? {}, onChange: onFilterChange }))] })) : (_jsx(BlogIndexControls, { basePath: basePath, query: query, category: category, tag: tag, sort: sort, categories: resolvedCategories, tags: resolvedTags })), result.totalItems === 0 && (_jsxs("section", { className: "cedros-site__card cedros-site__empty-state", children: [_jsx("h2", { className: "cedros-site__title", style: { fontSize: "1.25rem" }, children: "No posts found" }), _jsx("p", { className: "cedros-site__subtitle", style: { marginTop: "0.6rem" }, children: "Adjust filters or search terms to find matching articles." })] })), result.totalItems > 0 && (_jsxs(_Fragment, { children: [_jsx("section", { className: "cedros-site__content-grid", children: result.items.map((post) => (_jsxs("article", { className: "cedros-site__card cedros-site__entry-card", children: [_jsxs("div", { className: "cedros-site__entry-header", children: [_jsx("h2", { className: "cedros-site__entry-title", children: _jsx("a", { href: `${basePath}/${post.slug}`, children: post.title }) }), bookmarkedSlugs && onBookmarkToggle ? (_jsxs("div", { style: { display: "inline-flex", gap: "0.35rem", alignItems: "center" }, children: [post.publishedAt && _jsx("span", { className: "cedros-site__pill", children: post.publishedAt }), _jsx(BookmarkButton, { slug: post.slug, isBookmarked: bookmarkedSlugs.has(post.slug), onToggle: onBookmarkToggle })] })) : (post.publishedAt && _jsx("span", { className: "cedros-site__pill", children: post.publishedAt }))] }), (post.author || post.readingMinutes) && (_jsx("p", { className: "cedros-site__entry-meta", children: [post.author, readingTime(post.readingMinutes)].filter(Boolean).join(" • ") })), post.excerpt && _jsx("p", { className: "cedros-site__subtitle", children: post.excerpt }), post.tags && post.tags.length > 0 && (_jsx("div", { className: "cedros-site__tag-list", children: post.tags.map((entryTag) => (_jsx("a", { href: buildContentListHref(basePath, { q: query, category, tag: entryTag, sort }), className: "cedros-site__pill", children: entryTag }, entryTag))) }))] }, post.slug))) }), _jsx(ContentPagination, { basePath: basePath, page: result.page, totalPages: result.totalPages, query: { q: query, category, tag, sort } })] }))] }));
24
+ }
25
+ export function BlogPostTemplate({ siteTitle, navigation, title, bodyMarkdown, bodyHtml, allowUnsafeHtmlFallback = false, publishedAt, updatedAt, author, category, tags = [], readingMinutes, canonicalUrl, breadcrumbs = [], relatedPosts = [], basePath = "/blog", isBookmarked, onBookmarkToggle, slug, tipping, paywall }) {
26
+ const metadata = [publishedAt, updatedAt ? `Updated ${updatedAt}` : "", author, readingTime(readingMinutes)].filter(Boolean);
27
+ const showBookmark = isBookmarked !== undefined && onBookmarkToggle && slug;
28
+ return (_jsxs(SiteLayout, { siteTitle: siteTitle, navigation: navigation, children: [_jsx(Breadcrumbs, { trail: breadcrumbs }), _jsxs("article", { className: "cedros-site__card cedros-site__article", children: [_jsxs("div", { className: "cedros-site__article-header", children: [showBookmark ? (_jsxs("div", { style: { display: "inline-flex", gap: "0.35rem", alignItems: "center" }, children: [_jsx("span", { className: "cedros-site__pill", children: "blog" }), _jsx(BookmarkButton, { slug: slug, isBookmarked: isBookmarked, onToggle: onBookmarkToggle })] })) : (_jsx("span", { className: "cedros-site__pill", children: "blog" })), _jsx("h1", { className: "cedros-site__title", children: title }), metadata.length > 0 && _jsx("p", { className: "cedros-site__entry-meta", children: metadata.join(" • ") }), (category || tags.length > 0) && (_jsxs("div", { className: "cedros-site__tag-list", children: [category && (_jsx("a", { href: buildContentListHref(basePath, { category }), className: "cedros-site__pill", children: category })), tags.map((tag) => (_jsx("a", { href: buildContentListHref(basePath, { tag }), className: "cedros-site__pill", children: tag }, tag)))] })), canonicalUrl && (_jsx("a", { href: canonicalUrl, className: "cedros-site__subtitle", style: { marginTop: "0.5rem" }, children: "Canonical URL" }))] }), renderPaywallOrContent({
29
+ paywall,
30
+ bodyMarkdown,
31
+ bodyHtml,
32
+ allowUnsafeHtmlFallback
33
+ })] }), tipping?.enabled && (_jsx("section", { className: "cedros-site__card", children: _jsx(TipWidget, { recipient: tipping.allowPerPostRecipient && tipping.recipientOverride
34
+ ? tipping.recipientOverride
35
+ : tipping.recipient, currencies: tipping.currencies, presets: tipping.presets, payClient: tipping.payClient, label: tipping.label, description: tipping.description }) })), relatedPosts.length > 0 && (_jsxs("section", { className: "cedros-site__card", children: [_jsx("h2", { style: { margin: 0, fontSize: "1.02rem" }, children: "Related posts" }), _jsx("div", { className: "cedros-site__content-grid", style: { marginTop: "0.75rem" }, children: relatedPosts.map((post) => (_jsxs("article", { className: "cedros-site__entry-card", children: [_jsx("h3", { className: "cedros-site__entry-title", style: { marginTop: 0 }, children: _jsx("a", { href: `${basePath}/${post.slug}`, children: post.title }) }), post.excerpt && _jsx("p", { className: "cedros-site__subtitle", children: post.excerpt })] }, post.slug))) })] }))] }));
36
+ }
37
+ function BlogIndexControls({ basePath, query, category, tag, sort, categories, tags }) {
38
+ return (_jsxs("form", { method: "get", action: basePath, className: "cedros-site__controls cedros-site__card", children: [_jsxs("label", { className: "cedros-site__control", children: [_jsx("span", { children: "Search" }), _jsx("input", { type: "search", name: "q", defaultValue: query, placeholder: "Search blog posts" })] }), _jsxs("label", { className: "cedros-site__control", children: [_jsx("span", { children: "Category" }), _jsxs("select", { name: "category", defaultValue: category, children: [_jsx("option", { value: "", children: "All" }), categories.map((entry) => (_jsx("option", { value: entry, children: entry }, entry)))] })] }), _jsxs("label", { className: "cedros-site__control", children: [_jsx("span", { children: "Tag" }), _jsxs("select", { name: "tag", defaultValue: tag, children: [_jsx("option", { value: "", children: "All" }), tags.map((entry) => (_jsx("option", { value: entry, children: entry }, entry)))] })] }), _jsxs("label", { className: "cedros-site__control", children: [_jsx("span", { children: "Sort" }), _jsxs("select", { name: "sort", defaultValue: sort, children: [_jsx("option", { value: "newest", children: "Newest" }), _jsx("option", { value: "oldest", children: "Oldest" }), _jsx("option", { value: "title-asc", children: "Title A-Z" }), _jsx("option", { value: "title-desc", children: "Title Z-A" })] })] }), _jsxs("div", { className: "cedros-site__control-actions", children: [_jsx("button", { className: "cedros-site__nav-link", type: "submit", children: "Apply" }), _jsx("a", { className: "cedros-site__nav-link", href: basePath, children: "Clear" })] })] }));
39
+ }
40
+ function readingTime(minutes) {
41
+ if (!minutes) {
42
+ return "";
43
+ }
44
+ return `${minutes} min read`;
45
+ }
46
+ function renderPaywallOrContent({ paywall, bodyMarkdown, bodyHtml, allowUnsafeHtmlFallback }) {
47
+ const meteredUnlock = paywall?.remainingFreeReads !== undefined && paywall.remainingFreeReads > 0;
48
+ if (paywall && paywall.mode !== "free" && !paywall.unlocked && !meteredUnlock) {
49
+ const previewContent = paywall.mode === "preview" && bodyMarkdown
50
+ ? truncateToNParagraphs(bodyMarkdown, paywall.previewParagraphs ?? 3)
51
+ : undefined;
52
+ return (_jsx(ContentPaywall, { mode: paywall.mode, previewContent: previewContent, price: paywall.price, payClient: paywall.payClient, purchaseLabel: paywall.purchaseLabel, subscriptionLabel: paywall.subscriptionLabel }));
53
+ }
54
+ const content = (_jsx(MarkdownContent, { bodyMarkdown: bodyMarkdown, bodyHtml: bodyHtml, allowUnsafeHtmlFallback: allowUnsafeHtmlFallback }));
55
+ if (meteredUnlock && !paywall?.unlocked) {
56
+ const remaining = paywall.remainingFreeReads;
57
+ const label = remaining === 1 ? "1 free read remaining this month" : `${remaining} free reads remaining this month`;
58
+ return (_jsxs(_Fragment, { children: [content, _jsx("div", { className: "cedros-site__metered-banner", role: "status", children: label })] }));
59
+ }
60
+ return content;
61
+ }
62
+ function truncateToNParagraphs(markdown, n) {
63
+ return markdown.split(/\n\n+/).slice(0, n).join("\n\n");
64
+ }
@@ -0,0 +1,14 @@
1
+ import type { SiteNavigationItem } from "./SiteLayout.js";
2
+ export interface ContactDetail {
3
+ label: string;
4
+ value: string;
5
+ href?: string;
6
+ }
7
+ export interface ContactPageTemplateProps {
8
+ siteTitle: string;
9
+ navigation: SiteNavigationItem[];
10
+ title?: string;
11
+ description?: string;
12
+ details: ContactDetail[];
13
+ }
14
+ export declare function ContactPageTemplate({ siteTitle, navigation, title, description, details }: ContactPageTemplateProps): React.JSX.Element;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SiteLayout } from "./SiteLayout.js";
3
+ export function ContactPageTemplate({ siteTitle, navigation, title = "Contact", description = "", details }) {
4
+ return (_jsxs(SiteLayout, { siteTitle: siteTitle, navigation: navigation, children: [_jsxs("section", { className: "cedros-site__card", children: [_jsx("h1", { className: "cedros-site__title", children: title }), description && _jsx("p", { className: "cedros-site__subtitle", children: description })] }), _jsx("section", { style: { marginTop: "1rem", display: "grid", gap: "0.85rem" }, children: details.map((detail) => (_jsxs("article", { className: "cedros-site__card", children: [_jsx("h2", { style: { margin: 0, fontSize: "0.95rem" }, children: detail.label }), detail.href ? (_jsx("p", { className: "cedros-site__subtitle", style: { marginTop: "0.45rem" }, children: _jsx("a", { href: detail.href, children: detail.value }) })) : (_jsx("p", { className: "cedros-site__subtitle", style: { marginTop: "0.45rem" }, children: detail.value }))] }, detail.label))) })] }));
5
+ }
@@ -0,0 +1,29 @@
1
+ import { type DashboardNavItem } from "./DashboardShell.js";
2
+ import type { SiteFooterLink } from "./SiteFooter.js";
3
+ import type { SiteNavigationItem } from "./SiteLayout.js";
4
+ export interface DashboardStat {
5
+ key: string;
6
+ label: string;
7
+ value: string;
8
+ trend?: string;
9
+ }
10
+ export interface DashboardPanel {
11
+ key: string;
12
+ title: string;
13
+ description: string;
14
+ href?: string;
15
+ }
16
+ export interface DashboardOverviewTemplateProps {
17
+ siteTitle: string;
18
+ topNavigation?: SiteNavigationItem[];
19
+ sideNavigation: DashboardNavItem[];
20
+ currentPath?: string;
21
+ pageTitle?: string;
22
+ pageSubtitle?: string;
23
+ userName?: string;
24
+ userEmail?: string;
25
+ stats?: DashboardStat[];
26
+ panels?: DashboardPanel[];
27
+ footerLinks?: SiteFooterLink[];
28
+ }
29
+ export declare function DashboardOverviewTemplate({ siteTitle, topNavigation, sideNavigation, currentPath, pageTitle, pageSubtitle, userName, userEmail, stats, panels, footerLinks }: DashboardOverviewTemplateProps): React.JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { DashboardShell } from "./DashboardShell.js";
3
+ import { withActiveRouteState } from "./routing.js";
4
+ export function DashboardOverviewTemplate({ siteTitle, topNavigation = [], sideNavigation, currentPath, pageTitle = "Dashboard", pageSubtitle = "Manage content, schemas, and site settings from one place.", userName, userEmail, stats = [], panels = [], footerLinks = [] }) {
5
+ const activeSideNavigation = currentPath
6
+ ? withActiveRouteState(sideNavigation, currentPath)
7
+ : sideNavigation;
8
+ return (_jsxs(DashboardShell, { siteTitle: siteTitle, topNavigation: topNavigation, sideNavigation: activeSideNavigation, pageTitle: pageTitle, pageSubtitle: pageSubtitle, currentPath: currentPath, userName: userName, userEmail: userEmail, footerLinks: footerLinks, children: [stats.length > 0 && (_jsx("section", { style: {
9
+ display: "grid",
10
+ gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
11
+ gap: "0.75rem"
12
+ }, children: stats.map((stat) => (_jsxs("article", { className: "cedros-site__card", children: [_jsx("p", { style: { margin: 0, fontSize: "0.78rem", color: "var(--cds-muted)" }, children: stat.label }), _jsx("p", { style: { margin: "0.3rem 0 0", fontSize: "1.2rem", fontWeight: 600 }, children: stat.value }), stat.trend && (_jsx("p", { style: { margin: "0.3rem 0 0", fontSize: "0.75rem", color: "var(--cds-muted)" }, children: stat.trend }))] }, stat.key))) })), panels.length > 0 && (_jsx("section", { style: {
13
+ display: "grid",
14
+ gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
15
+ gap: "0.75rem"
16
+ }, children: panels.map((panel) => (_jsxs("article", { className: "cedros-site__card", style: { display: "grid", gap: "0.45rem" }, children: [_jsx("h2", { style: { margin: 0, fontSize: "0.95rem" }, children: panel.title }), _jsx("p", { className: "cedros-site__subtitle", style: { margin: 0, fontSize: "0.8rem" }, children: panel.description }), panel.href && (_jsx("div", { children: _jsx("a", { className: "cedros-site__nav-link", href: panel.href, children: "Open" }) }))] }, panel.key))) }))] }));
17
+ }
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ import { type SiteFooterLink } from "./SiteFooter.js";
3
+ import type { SiteNavigationItem } from "./SiteLayout.js";
4
+ export interface DashboardNavItem {
5
+ key: string;
6
+ label: string;
7
+ route: string;
8
+ description?: string;
9
+ isActive?: boolean;
10
+ badge?: string;
11
+ }
12
+ export interface DashboardShellProps {
13
+ siteTitle: string;
14
+ topNavigation?: SiteNavigationItem[];
15
+ sideNavigation: DashboardNavItem[];
16
+ pageTitle: string;
17
+ pageSubtitle?: string;
18
+ children: ReactNode;
19
+ brandHref?: string;
20
+ topRightSlot?: ReactNode;
21
+ pageActions?: ReactNode;
22
+ currentPath?: string;
23
+ userName?: string;
24
+ userEmail?: string;
25
+ footerNote?: string;
26
+ footerLinks?: SiteFooterLink[];
27
+ }
28
+ export declare function DashboardShell({ siteTitle, topNavigation, sideNavigation, pageTitle, pageSubtitle, children, brandHref, topRightSlot, pageActions, currentPath, userName, userEmail, footerNote, footerLinks }: DashboardShellProps): React.JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SiteFooter } from "./SiteFooter.js";
3
+ import { TopNav } from "./TopNav.js";
4
+ import { withActiveRouteState } from "./routing.js";
5
+ export function DashboardShell({ siteTitle, topNavigation = [], sideNavigation, pageTitle, pageSubtitle, children, brandHref = "/", topRightSlot, pageActions, currentPath, userName, userEmail, footerNote = "Powered by cedros-data", footerLinks = [] }) {
6
+ const activeSideNavigation = currentPath
7
+ ? withActiveRouteState(sideNavigation, currentPath)
8
+ : sideNavigation;
9
+ return (_jsxs("div", { className: "cedros-site cedros-site-dashboard", children: [_jsx(TopNav, { siteTitle: siteTitle, navigation: topNavigation, brandHref: brandHref, rightSlot: topRightSlot, currentPath: currentPath }), _jsx("main", { className: "cedros-site__main", children: _jsxs("div", { className: "cedros-site__container cedros-site-dashboard__layout", children: [_jsxs("aside", { className: "cedros-site-dashboard__sidebar", "aria-label": "Dashboard sections", children: [(userName || userEmail) && (_jsxs("section", { className: "cedros-site__card cedros-site-dashboard__profile", children: [userName && _jsx("h2", { className: "cedros-site-dashboard__profile-name", children: userName }), userEmail && _jsx("p", { className: "cedros-site-dashboard__profile-email", children: userEmail })] })), _jsx("nav", { className: "cedros-site-dashboard__side-nav", "aria-label": "Dashboard navigation", children: activeSideNavigation.map((item) => (_jsxs("a", { href: item.route, className: `cedros-site-dashboard__side-link${item.isActive ? " cedros-site-dashboard__side-link--active" : ""}`, "aria-current": item.isActive ? "page" : undefined, children: [_jsx("span", { children: item.label }), item.badge && _jsx("span", { className: "cedros-site-dashboard__badge", children: item.badge }), item.description && (_jsx("small", { className: "cedros-site-dashboard__side-description", children: item.description }))] }, item.key))) })] }), _jsxs("section", { className: "cedros-site-dashboard__content", children: [_jsxs("header", { className: "cedros-site-dashboard__content-header", children: [_jsxs("div", { children: [_jsx("h1", { className: "cedros-site__title cedros-site-dashboard__title", children: pageTitle }), pageSubtitle && _jsx("p", { className: "cedros-site__subtitle", children: pageSubtitle })] }), pageActions && _jsx("div", { className: "cedros-site-dashboard__actions", children: pageActions })] }), _jsx("div", { className: "cedros-site-dashboard__panel", children: children })] })] }) }), _jsx(SiteFooter, { siteTitle: siteTitle, note: footerNote, links: footerLinks })] }));
10
+ }
@@ -0,0 +1,14 @@
1
+ import type { DocsSidebarSection } from "./docsNavigation.js";
2
+ export interface DocsSidebarProps {
3
+ title: string;
4
+ basePath: string;
5
+ searchQuery: string;
6
+ sections: DocsSidebarSection[];
7
+ }
8
+ /**
9
+ * Docs sidebar with collapsible sections and mobile drawer toggle.
10
+ *
11
+ * Desktop: always visible, sticky.
12
+ * Mobile: hidden behind a hamburger button; overlay closes on link click.
13
+ */
14
+ export declare function DocsSidebar({ title, basePath, searchQuery, sections, }: DocsSidebarProps): React.JSX.Element;
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ /**
5
+ * Docs sidebar with collapsible sections and mobile drawer toggle.
6
+ *
7
+ * Desktop: always visible, sticky.
8
+ * Mobile: hidden behind a hamburger button; overlay closes on link click.
9
+ */
10
+ export function DocsSidebar({ title, basePath, searchQuery, sections, }) {
11
+ const [open, setOpen] = useState(false);
12
+ return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "cedros-site__sidebar-toggle", "aria-label": "Toggle sidebar", "aria-expanded": open, onClick: () => setOpen((prev) => !prev), children: title }), open && (_jsx("div", { className: "cedros-site__sidebar-overlay", onClick: () => setOpen(false), role: "presentation" })), _jsxs("aside", { className: `cedros-site__card cedros-site__docs-sidebar${open ? " cedros-site__docs-sidebar--open" : ""}`, children: [_jsx("h2", { className: "cedros-site__docs-sidebar-title", children: title }), _jsx("form", { method: "get", action: basePath, className: "cedros-site__docs-search", children: _jsxs("label", { className: "cedros-site__control", children: [_jsx("span", { children: "Search docs" }), _jsx("input", { type: "search", name: "q", defaultValue: searchQuery, placeholder: "Search docs..." })] }) }), _jsx("a", { href: basePath, className: "cedros-site__docs-root-link", children: "Browse all docs" }), sections.length === 0 && (_jsx("p", { className: "cedros-site__entry-meta", children: "No sections available yet." })), sections.map((section) => (_jsxs("details", { className: "cedros-site__docs-section", open: section.collapsed !== true, children: [_jsx("summary", { className: "cedros-site__docs-section-title", children: section.label }), _jsx("nav", { className: "cedros-site__docs-section-links", "aria-label": section.label, children: section.items.map((item) => (_jsxs("a", { href: item.href, className: `cedros-site__docs-item cedros-site__docs-item--depth-${item.depth ?? 0}${item.isActive ? " cedros-site__docs-item--active" : ""}`, "aria-current": item.isActive ? "page" : undefined, onClick: () => setOpen(false), children: [_jsx("span", { children: item.label }), item.badge && (_jsx("span", { className: "cedros-site__docs-item-badge", children: item.badge }))] }, item.key))) })] }, section.key)))] })] }));
13
+ }