specra 0.1.0 → 0.1.1

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 (42) hide show
  1. package/dist/app/docs-page.js +164 -148
  2. package/dist/app/docs-page.js.map +1 -1
  3. package/dist/app/docs-page.mjs +3 -3
  4. package/dist/app/layout.js +84 -5
  5. package/dist/app/layout.js.map +1 -1
  6. package/dist/app/layout.mjs +3 -3
  7. package/dist/{chunk-NXRIAL7T.mjs → chunk-4QHOMV5A.mjs} +8 -5
  8. package/dist/chunk-4QHOMV5A.mjs.map +1 -0
  9. package/dist/{chunk-IZFGEAD6.mjs → chunk-5AYOV2KD.mjs} +5 -4
  10. package/dist/chunk-5AYOV2KD.mjs.map +1 -0
  11. package/dist/chunk-I3ELVEK2.mjs +56 -0
  12. package/dist/chunk-I3ELVEK2.mjs.map +1 -0
  13. package/dist/{chunk-INL2EC72.mjs → chunk-RLMSINLY.mjs} +2 -5
  14. package/dist/{chunk-INL2EC72.mjs.map → chunk-RLMSINLY.mjs.map} +1 -1
  15. package/dist/components/index.d.mts +39 -3
  16. package/dist/components/index.d.ts +39 -3
  17. package/dist/components/index.js +100 -62
  18. package/dist/components/index.js.map +1 -1
  19. package/dist/components/index.mjs +83 -48
  20. package/dist/components/index.mjs.map +1 -1
  21. package/dist/index.d.mts +39 -3
  22. package/dist/index.d.ts +39 -3
  23. package/dist/index.js +129 -94
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.mjs +10 -4
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/lib/index.d.mts +7 -0
  28. package/dist/lib/index.d.ts +7 -0
  29. package/dist/lib/index.js +1 -4
  30. package/dist/lib/index.js.map +1 -1
  31. package/dist/lib/index.mjs +1 -1
  32. package/package.json +2 -2
  33. package/src/app/layout.tsx +6 -3
  34. package/src/components/docs/header.tsx +7 -3
  35. package/src/components/docs/index.ts +3 -0
  36. package/src/lib/config.context.tsx +65 -0
  37. package/src/lib/config.server.ts +9 -5
  38. package/src/lib/index.ts +1 -0
  39. package/dist/chunk-IZFGEAD6.mjs.map +0 -1
  40. package/dist/chunk-MZJHJ6BV.mjs +0 -21
  41. package/dist/chunk-MZJHJ6BV.mjs.map +0 -1
  42. package/dist/chunk-NXRIAL7T.mjs.map +0 -1
@@ -1,10 +1,11 @@
1
1
  import {
2
+ ConfigProvider,
2
3
  TabProvider
3
- } from "./chunk-MZJHJ6BV.mjs";
4
+ } from "./chunk-I3ELVEK2.mjs";
4
5
  import {
5
6
  getAssetPath,
6
7
  getConfig
7
- } from "./chunk-INL2EC72.mjs";
8
+ } from "./chunk-RLMSINLY.mjs";
8
9
 
9
10
  // src/app/layout.tsx
10
11
  import { Geist, Geist_Mono } from "next/font/google";
@@ -50,7 +51,7 @@ function RootLayout({
50
51
  }) {
51
52
  const config = getConfig();
52
53
  const defaultTab = config.navigation?.tabGroups?.[0]?.id || "";
53
- return /* @__PURE__ */ jsx("html", { lang: config.site.language || "en", suppressHydrationWarning: true, children: /* @__PURE__ */ jsx("body", { className: `${geist.className} font-sans antialiased`, children: /* @__PURE__ */ jsx(TabProvider, { defaultTab, children }) }) });
54
+ return /* @__PURE__ */ jsx("html", { lang: config.site.language || "en", suppressHydrationWarning: true, children: /* @__PURE__ */ jsx("body", { className: `${geist.className} font-sans antialiased`, children: /* @__PURE__ */ jsx(ConfigProvider, { config, children: /* @__PURE__ */ jsx(TabProvider, { defaultTab, children }) }) }) });
54
55
  }
55
56
 
56
57
  export {
@@ -58,4 +59,4 @@ export {
58
59
  metadata,
59
60
  RootLayout
60
61
  };
61
- //# sourceMappingURL=chunk-IZFGEAD6.mjs.map
62
+ //# sourceMappingURL=chunk-5AYOV2KD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/app/layout.tsx"],"sourcesContent":["import type React from \"react\"\nimport type { Metadata } from \"next\"\nimport { Geist, Geist_Mono } from \"next/font/google\"\nimport { getConfig } from \"../lib/config\"\nimport { getAssetPath } from \"../lib/utils\"\nimport { ConfigProvider } from \"../lib/config.context\"\nimport { TabProvider } from \"../components/docs/tab-context\"\nimport \"../styles/globals.css\"\n\nconst geist = Geist({ subsets: [\"latin\"] })\nconst geistMono = Geist_Mono({ subsets: [\"latin\"] })\n\n/**\n * Generate metadata for the root layout\n * This can be imported and used by the user's app/layout.tsx\n */\nexport function generateMetadata(): Metadata {\n const config = getConfig()\n\n return {\n title: {\n default: config.site.title,\n template: `%s | ${config.site.title}`,\n },\n description: config.site.description || \"Modern documentation platform\",\n generator: \"Specra Documentation\",\n metadataBase: config.site.url ? new URL(config.site.url) : undefined,\n icons: {\n icon: getAssetPath(config.site.favicon ?? \"\") ? [\n {\n url: getAssetPath(config.site.favicon ?? \"\"),\n },\n ] : [],\n apple: getAssetPath(\"/apple-icon.png\"),\n },\n openGraph: {\n title: config.site.title,\n description: config.site.description,\n url: config.site.url,\n siteName: config.site.title,\n locale: config.site.language || \"en\",\n type: \"website\",\n },\n twitter: {\n card: \"summary_large_image\",\n title: config.site.title,\n description: config.site.description,\n },\n }\n}\n\nexport const metadata: Metadata = generateMetadata()\n\n/**\n * Root layout component for Specra documentation sites\n * This provides the HTML structure and global providers\n */\nexport default function RootLayout({\n children,\n}: Readonly<{\n children: React.ReactNode\n}>) {\n const config = getConfig()\n const defaultTab = config.navigation?.tabGroups?.[0]?.id || \"\"\n\n return (\n <html lang={config.site.language || \"en\"} suppressHydrationWarning>\n <body className={`${geist.className} font-sans antialiased`}>\n <ConfigProvider config={config}>\n <TabProvider defaultTab={defaultTab}>\n {children}\n </TabProvider>\n </ConfigProvider>\n </body>\n </html>\n )\n}"],"mappings":";;;;;;;;;;AAEA,SAAS,OAAO,kBAAkB;AAmExB;AA5DV,IAAM,QAAQ,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;AAC1C,IAAM,YAAY,WAAW,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;AAM5C,SAAS,mBAA6B;AAC3C,QAAM,SAAS,UAAU;AAEzB,SAAO;AAAA,IACL,OAAO;AAAA,MACL,SAAS,OAAO,KAAK;AAAA,MACrB,UAAU,QAAQ,OAAO,KAAK,KAAK;AAAA,IACrC;AAAA,IACA,aAAa,OAAO,KAAK,eAAe;AAAA,IACxC,WAAW;AAAA,IACX,cAAc,OAAO,KAAK,MAAM,IAAI,IAAI,OAAO,KAAK,GAAG,IAAI;AAAA,IAC3D,OAAO;AAAA,MACL,MAAM,aAAa,OAAO,KAAK,WAAW,EAAE,IAAI;AAAA,QAC9C;AAAA,UACE,KAAK,aAAa,OAAO,KAAK,WAAW,EAAE;AAAA,QAC7C;AAAA,MACF,IAAI,CAAC;AAAA,MACL,OAAO,aAAa,iBAAiB;AAAA,IACvC;AAAA,IACA,WAAW;AAAA,MACT,OAAO,OAAO,KAAK;AAAA,MACnB,aAAa,OAAO,KAAK;AAAA,MACzB,KAAK,OAAO,KAAK;AAAA,MACjB,UAAU,OAAO,KAAK;AAAA,MACtB,QAAQ,OAAO,KAAK,YAAY;AAAA,MAChC,MAAM;AAAA,IACR;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,OAAO,OAAO,KAAK;AAAA,MACnB,aAAa,OAAO,KAAK;AAAA,IAC3B;AAAA,EACF;AACF;AAEO,IAAM,WAAqB,iBAAiB;AAMpC,SAAR,WAA4B;AAAA,EACjC;AACF,GAEI;AACF,QAAM,SAAS,UAAU;AACzB,QAAM,aAAa,OAAO,YAAY,YAAY,CAAC,GAAG,MAAM;AAE5D,SACE,oBAAC,UAAK,MAAM,OAAO,KAAK,YAAY,MAAM,0BAAwB,MAChE,8BAAC,UAAK,WAAW,GAAG,MAAM,SAAS,0BACjC,8BAAC,kBAAe,QACd,8BAAC,eAAY,YACV,UACH,GACF,GACF,GACF;AAEJ;","names":[]}
@@ -0,0 +1,56 @@
1
+ import {
2
+ defaultConfig
3
+ } from "./chunk-RLMSINLY.mjs";
4
+
5
+ // src/components/docs/tab-context.tsx
6
+ import { createContext, useContext, useState } from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var TabContext = createContext(void 0);
9
+ function TabProvider({ children, defaultTab }) {
10
+ const [activeTabGroup, setActiveTabGroup] = useState(defaultTab);
11
+ return /* @__PURE__ */ jsx(TabContext.Provider, { value: { activeTabGroup, setActiveTabGroup }, children });
12
+ }
13
+ function useTabContext() {
14
+ const context = useContext(TabContext);
15
+ if (!context) {
16
+ throw new Error("useTabContext must be used within TabProvider");
17
+ }
18
+ return context;
19
+ }
20
+
21
+ // src/lib/config.context.tsx
22
+ import * as React from "react";
23
+ import { jsx as jsx2 } from "react/jsx-runtime";
24
+ var ConfigContext = React.createContext(defaultConfig);
25
+ function ConfigProvider({ config, children }) {
26
+ return /* @__PURE__ */ jsx2(ConfigContext.Provider, { value: config, children });
27
+ }
28
+ function useConfig() {
29
+ const config = React.useContext(ConfigContext);
30
+ if (!config) {
31
+ throw new Error("useConfig must be used within a ConfigProvider");
32
+ }
33
+ return config;
34
+ }
35
+ function useConfigValue(path) {
36
+ const config = useConfig();
37
+ const keys = path.split(".");
38
+ let value = config;
39
+ for (const key of keys) {
40
+ if (value && typeof value === "object" && key in value) {
41
+ value = value[key];
42
+ } else {
43
+ return void 0;
44
+ }
45
+ }
46
+ return value;
47
+ }
48
+
49
+ export {
50
+ TabProvider,
51
+ useTabContext,
52
+ ConfigProvider,
53
+ useConfig,
54
+ useConfigValue
55
+ };
56
+ //# sourceMappingURL=chunk-I3ELVEK2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/docs/tab-context.tsx","../src/lib/config.context.tsx"],"sourcesContent":["\"use client\"\n\nimport { createContext, useContext, useState, ReactNode } from \"react\"\n\ninterface TabContextType {\n activeTabGroup: string\n setActiveTabGroup: (tabId: string) => void\n}\n\nconst TabContext = createContext<TabContextType | undefined>(undefined)\n\nexport function TabProvider({ children, defaultTab }: { children: ReactNode; defaultTab: string }) {\n const [activeTabGroup, setActiveTabGroup] = useState(defaultTab)\n\n return (\n <TabContext.Provider value={{ activeTabGroup, setActiveTabGroup }}>\n {children}\n </TabContext.Provider>\n )\n}\n\nexport function useTabContext() {\n const context = useContext(TabContext)\n if (!context) {\n throw new Error(\"useTabContext must be used within TabProvider\")\n }\n return context\n}\n","import * as React from \"react\"\nimport { SpecraConfig, defaultConfig } from \"./config.types\"\n\nconst ConfigContext = React.createContext<SpecraConfig>(defaultConfig)\n\nexport interface ConfigProviderProps {\n config: SpecraConfig\n children: React.ReactNode\n}\n\n/**\n * Provider component that makes Specra config available to all client components\n * Usage: Wrap your app with this provider in your root layout\n */\nexport function ConfigProvider({ config, children }: ConfigProviderProps) {\n return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>\n}\n\n/**\n * Hook to access Specra configuration in any client component\n * @returns The current Specra configuration\n * @example\n * ```tsx\n * function MyComponent() {\n * const config = useConfig()\n * return <div>{config.site.title}</div>\n * }\n * ```\n */\nexport function useConfig(): SpecraConfig {\n const config = React.useContext(ConfigContext)\n if (!config) {\n throw new Error(\"useConfig must be used within a ConfigProvider\")\n }\n return config\n}\n\n/**\n * Hook to access a specific configuration value by path\n * @param path - Dot-separated path to the config value (e.g., \"site.title\")\n * @returns The configuration value at the specified path\n * @example\n * ```tsx\n * function MyComponent() {\n * const title = useConfigValue<string>(\"site.title\")\n * const showSidebar = useConfigValue<boolean>(\"navigation.showSidebar\")\n * return <div>{title}</div>\n * }\n * ```\n */\nexport function useConfigValue<T = any>(path: string): T | undefined {\n const config = useConfig()\n const keys = path.split(\".\")\n let value: any = config\n\n for (const key of keys) {\n if (value && typeof value === \"object\" && key in value) {\n value = value[key]\n } else {\n return undefined\n }\n }\n\n return value as T\n}\n"],"mappings":";;;;;AAEA,SAAS,eAAe,YAAY,gBAA2B;AAa3D;AANJ,IAAM,aAAa,cAA0C,MAAS;AAE/D,SAAS,YAAY,EAAE,UAAU,WAAW,GAAgD;AACjG,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,UAAU;AAE/D,SACE,oBAAC,WAAW,UAAX,EAAoB,OAAO,EAAE,gBAAgB,kBAAkB,GAC7D,UACH;AAEJ;AAEO,SAAS,gBAAgB;AAC9B,QAAM,UAAU,WAAW,UAAU;AACrC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AACA,SAAO;AACT;;;AC3BA,YAAY,WAAW;AAed,gBAAAA,YAAA;AAZT,IAAM,gBAAsB,oBAA4B,aAAa;AAW9D,SAAS,eAAe,EAAE,QAAQ,SAAS,GAAwB;AACxE,SAAO,gBAAAA,KAAC,cAAc,UAAd,EAAuB,OAAO,QAAS,UAAS;AAC1D;AAaO,SAAS,YAA0B;AACxC,QAAM,SAAe,iBAAW,aAAa;AAC7C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AACA,SAAO;AACT;AAeO,SAAS,eAAwB,MAA6B;AACnE,QAAM,SAAS,UAAU;AACzB,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,MAAI,QAAa;AAEjB,aAAW,OAAO,MAAM;AACtB,QAAI,SAAS,OAAO,UAAU,YAAY,OAAO,OAAO;AACtD,cAAQ,MAAM,GAAG;AAAA,IACnB,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;","names":["jsx"]}
@@ -120,15 +120,12 @@ function validateConfig(config) {
120
120
  }
121
121
  var configInstance = null;
122
122
  function initConfig(userConfig) {
123
- if (configInstance) {
124
- throw new Error("Specra config has already been initialized");
125
- }
126
123
  configInstance = loadConfig(userConfig);
127
124
  return configInstance;
128
125
  }
129
126
  function getConfig() {
130
127
  if (!configInstance) {
131
- throw new Error("Specra config has not been initialized");
128
+ configInstance = loadConfig({});
132
129
  }
133
130
  return configInstance;
134
131
  }
@@ -167,4 +164,4 @@ export {
167
164
  cn,
168
165
  getAssetPath
169
166
  };
170
- //# sourceMappingURL=chunk-INL2EC72.mjs.map
167
+ //# sourceMappingURL=chunk-RLMSINLY.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/config.types.ts","../src/lib/config.server.ts","../src/lib/utils.ts"],"sourcesContent":["/**\n * Configuration schema for Specra documentation system\n */\n\n/**\n * Site metadata and branding\n */\nexport interface SiteConfig {\n /** The title of the documentation site */\n title: string\n /** Short description of the documentation */\n description?: string\n /** URL where the documentation is hosted */\n url?: string\n /** Base URL path for the documentation (e.g., '/docs') */\n baseUrl?: string\n /** Path to the site logo */\n logo?: string\n /** Path to the favicon */\n favicon?: string\n /** Default language for the documentation */\n language?: string\n /** Organization or author name */\n organizationName?: string\n /** Project name */\n projectName?: string\n /** Active/default version for the documentation */\n activeVersion?: string\n}\n\n/**\n * Theme and appearance settings\n */\nexport interface ThemeConfig {\n /** Primary color for the theme */\n primaryColor?: string\n /** Default theme mode */\n defaultMode?: \"light\" | \"dark\" | \"system\"\n /** Whether to respect system preferences */\n respectPrefersColorScheme?: boolean\n /** Custom CSS file path */\n customCss?: string\n}\n\n/**\n * Tab group for organizing documentation\n */\nexport interface TabGroup {\n /** Unique identifier for the tab group */\n id: string\n /** Display label for the tab */\n label: string\n /** Optional icon name (lucide-react icon) */\n icon?: string\n}\n\n/**\n * Navigation and sidebar configuration\n */\nexport interface NavigationConfig {\n /** Whether to show the sidebar by default */\n showSidebar?: boolean\n /** Whether the sidebar is collapsible */\n collapsibleSidebar?: boolean\n /** Whether to show breadcrumbs */\n showBreadcrumbs?: boolean\n /** Whether to show table of contents */\n showTableOfContents?: boolean\n /** Position of table of contents */\n tocPosition?: \"left\" | \"right\"\n /** Maximum depth for table of contents */\n tocMaxDepth?: number\n /** Tab groups for organizing documentation sections */\n tabGroups?: TabGroup[]\n}\n\n/**\n * Social and external links\n */\nexport interface SocialLinks {\n /** GitHub repository URL */\n github?: string\n /** Twitter/X handle or URL */\n twitter?: string\n /** Discord invite URL */\n discord?: string\n /** LinkedIn profile or company page */\n linkedin?: string\n /** YouTube channel URL */\n youtube?: string\n /** Custom social links */\n custom?: Array<{\n label: string\n url: string\n icon?: string\n }>\n}\n\n/**\n * Search configuration\n */\nexport interface SearchConfig {\n /** Enable/disable search functionality */\n enabled?: boolean\n /** Placeholder text for search input */\n placeholder?: string\n /** Search provider type */\n provider?: \"meilisearch\" | \"algolia\" | \"local\"\n /** Meilisearch configuration */\n meilisearch?: {\n /** Meilisearch server URL */\n host: string\n /** API key for Meilisearch */\n apiKey?: string\n /** Index name */\n indexName: string\n }\n}\n\n/**\n * Analytics configuration\n */\nexport interface AnalyticsConfig {\n /** Google Analytics tracking ID */\n googleAnalytics?: string\n /** Google Tag Manager ID */\n googleTagManager?: string\n /** Plausible Analytics domain */\n plausible?: string\n /** Custom analytics scripts */\n custom?: Array<{\n src: string\n async?: boolean\n defer?: boolean\n }>\n}\n\n/**\n * Footer configuration\n */\nexport interface FooterConfig {\n /** Copyright text */\n copyright?: string\n /** Footer links organized by columns */\n links?: Array<{\n title: string\n items: Array<{\n label: string\n href: string\n }>\n }>\n /** Custom footer content */\n customContent?: string\n}\n\n/**\n * Documentation features\n */\nexport interface FeaturesConfig {\n /** Enable/disable edit this page links */\n editUrl?: string | false\n /** Show last updated timestamp */\n showLastUpdated?: boolean\n /** Show reading time estimate */\n showReadingTime?: boolean\n /** Show author information */\n showAuthors?: boolean\n /** Show tags */\n showTags?: boolean\n /** Enable version dropdown */\n versioning?: boolean\n /** Enable i18n (internationalization) */\n i18n?: boolean\n}\n\n/**\n * Site-wide banner configuration\n */\nexport interface BannerConfig {\n /** Whether the banner is enabled */\n enabled?: boolean\n /** Banner message */\n message?: string\n /** Banner type */\n type?: \"info\" | \"warning\" | \"success\" | \"error\"\n /** Whether the banner can be dismissed */\n dismissible?: boolean\n}\n\n/**\n * Environment variables that can be used in documentation\n * These will be replaced at build time or runtime\n */\nexport interface EnvironmentVariables {\n /** API base URL */\n API_BASE_URL?: string\n /** API version */\n API_VERSION?: string\n /** CDN URL */\n CDN_URL?: string\n /** Custom environment variables */\n [key: string]: string | undefined\n}\n\n/**\n * Deployment configuration for different hosting scenarios\n */\nexport interface DeploymentConfig {\n /**\n * Deployment target\n * - 'vercel': For Vercel or similar Node.js hosting (uses 'standalone' output)\n * - 'github-pages': For GitHub Pages static hosting (uses 'export' output)\n * - 'static': For any static hosting like Netlify, Cloudflare Pages, etc.\n * - 'custom-domain-static': For static hosting with custom domain (no basePath needed)\n */\n target?: \"vercel\" | \"github-pages\" | \"static\" | \"custom-domain-static\"\n\n /**\n * Base path for assets when deploying to GitHub Pages without custom domain\n * This should be your repository name (e.g., 'my-repo')\n * Only used when target is 'github-pages' and no custom domain is configured\n */\n basePath?: string\n\n /**\n * Whether a custom domain is configured\n * When true, basePath will be ignored even for GitHub Pages\n */\n customDomain?: boolean\n}\n\n/**\n * Main configuration interface\n */\nexport interface SpecraConfig {\n /** Site metadata and branding */\n site: SiteConfig\n /** Theme and appearance settings */\n theme?: ThemeConfig\n /** Navigation and sidebar configuration */\n navigation?: NavigationConfig\n /** Social and external links */\n social?: SocialLinks\n /** Search configuration */\n search?: SearchConfig\n /** Analytics configuration */\n analytics?: AnalyticsConfig\n /** Footer configuration */\n footer?: FooterConfig\n /** Site-wide banner */\n banner?: BannerConfig\n /** Documentation features */\n features?: FeaturesConfig\n /** Environment variables for use in docs */\n env?: EnvironmentVariables\n /** Deployment configuration */\n deployment?: DeploymentConfig\n}\n\n/**\n * Default configuration values\n */\nexport const defaultConfig: SpecraConfig = {\n site: {\n title: \"Documentation\",\n description: \"Project documentation\",\n baseUrl: \"/\",\n language: \"en\",\n },\n theme: {\n defaultMode: \"system\",\n respectPrefersColorScheme: true,\n },\n navigation: {\n showSidebar: true,\n collapsibleSidebar: true,\n showBreadcrumbs: true,\n showTableOfContents: true,\n tocPosition: \"right\",\n tocMaxDepth: 3,\n },\n search: {\n enabled: true,\n provider: \"local\",\n placeholder: \"Search documentation...\",\n },\n features: {\n showLastUpdated: true,\n showReadingTime: true,\n showAuthors: false,\n showTags: true,\n versioning: true,\n i18n: false,\n },\n}\n","import specraConfigJson from \"../specra.config.json\"\nimport { SpecraConfig, defaultConfig } from \"./config.types\"\n\n/**\n * Deep merge two objects\n */\nfunction deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T {\n const result = { ...target }\n\n for (const key in source) {\n const sourceValue = source[key]\n const targetValue = result[key]\n\n if (sourceValue && typeof sourceValue === \"object\" && !Array.isArray(sourceValue)) {\n result[key] = deepMerge(\n targetValue && typeof targetValue === \"object\" ? targetValue : {},\n sourceValue,\n ) as T[Extract<keyof T, string>]\n } else if (sourceValue !== undefined) {\n result[key] = sourceValue as T[Extract<keyof T, string>]\n }\n }\n\n return result\n}\n\n/**\n * Load and parse the Specra configuration file\n * Falls back to default configuration if file doesn't exist or is invalid\n */\nexport function loadConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n try {\n // const userConfig = specraConfigJson as unknown as Partial<SpecraConfig>\n\n // Merge user config with defaults \n const config = deepMerge(defaultConfig, userConfig)\n\n return config\n } catch (error) {\n console.error(`❌ Error loading configuration:`, error)\n console.warn(\"Using default configuration.\")\n return defaultConfig\n }\n}\n\n/**\n * Get a specific configuration value by path (SERVER ONLY)\n * Example: getConfigValue('site.title') or getConfigValue('theme.defaultMode')\n */\nexport function getConfigValue<T = any>(path: string, config?: SpecraConfig): T | undefined {\n const cfg = config || loadConfig({})\n const keys = path.split(\".\")\n let value: any = cfg\n\n for (const key of keys) {\n if (value && typeof value === \"object\" && key in value) {\n value = value[key]\n } else {\n return undefined\n }\n }\n\n return value as T\n}\n\n/**\n * Replace environment variables in a string (SERVER ONLY)\n * Supports ${ENV_VAR} and {{ENV_VAR}} syntax\n */\nexport function replaceEnvVariables(text: string, config?: SpecraConfig): string {\n const cfg = config || loadConfig({})\n const envVars = cfg.env || {}\n\n let result = text\n\n // Replace ${VAR} syntax\n result = result.replace(/\\$\\{([^}]+)\\}/g, (match, varName) => {\n return envVars[varName] || match\n })\n\n // Replace {{VAR}} syntax\n result = result.replace(/\\{\\{([^}]+)\\}\\}/g, (match, varName) => {\n return envVars[varName] || match\n })\n\n return result\n}\n\n/**\n * Process content and replace all environment variables (SERVER ONLY)\n */\nexport function processContentWithEnv(content: string, config?: SpecraConfig): string {\n return replaceEnvVariables(content, config)\n}\n\n/**\n * Validate configuration (basic validation) (SERVER ONLY)\n */\nexport function validateConfig(config: SpecraConfig): { valid: boolean; errors: string[] } {\n const errors: string[] = []\n\n // Required fields\n if (!config.site?.title) {\n errors.push(\"site.title is required\")\n }\n\n // URL validation\n if (config.site?.url) {\n try {\n new URL(config.site.url)\n } catch {\n errors.push(\"site.url must be a valid URL\")\n }\n }\n\n // Social links validation\n if (config.social) {\n const socialKeys = [\"github\", \"twitter\", \"discord\", \"linkedin\", \"youtube\"] as const\n for (const key of socialKeys) {\n const url = config.social[key]\n if (url) {\n try {\n new URL(url)\n } catch {\n errors.push(`social.${key} must be a valid URL`)\n }\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n// Singleton instance\nlet configInstance: SpecraConfig | null = null\n\nexport function initConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n if (configInstance) {\n throw new Error(\"Specra config has already been initialized\")\n }\n\n configInstance = loadConfig(userConfig)\n return configInstance\n}\n\n/**\n * Get the configuration instance (cached) (SERVER ONLY)\n */\nexport function getConfig(): SpecraConfig {\n if (!configInstance) {\n throw new Error(\"Specra config has not been initialized\")\n }\n return configInstance\n}\n\n/**\n * Reload the configuration (useful for development) (SERVER ONLY)\n */\nexport function reloadConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n configInstance = loadConfig(userConfig)\n return configInstance\n}\n\n/**\n * Export the loaded config as default (SERVER ONLY)\n */\n// export default getConfig()\n","import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\n\n/**\n * Get the correct asset path based on deployment configuration\n * Handles different deployment scenarios:\n * - Vercel/Node.js hosting (standalone build): No basePath needed\n * - GitHub Pages without custom domain: Uses basePath from config\n * - Static hosting with custom domain: No basePath needed\n *\n * @param path - The asset path (can start with or without '/')\n * @returns The properly formatted asset path\n */\nexport function getAssetPath(path: string): string {\n // Get basePath from Next.js config (set during build for static exports)\n const basePath = process.env.NEXT_PUBLIC_BASE_PATH || process.env.__NEXT_ROUTER_BASEPATH || ''\n\n // Normalize the input path: ensure it starts with '/'\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n\n // If we have a basePath (GitHub Pages without custom domain), prepend it\n if (basePath) {\n // Normalize basePath: remove trailing slash, ensure leading slash\n const normalizedBase = basePath.startsWith('/') ? basePath : `/${basePath}`\n const cleanBase = normalizedBase.replace(/\\/$/, '')\n return `${cleanBase}${normalizedPath}`\n }\n\n // Default: return the normalized path (works for Vercel, custom domains, and dev)\n return normalizedPath\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/lib/config.types.ts","../src/lib/config.server.ts","../src/lib/utils.ts"],"sourcesContent":["/**\n * Configuration schema for Specra documentation system\n */\n\n/**\n * Site metadata and branding\n */\nexport interface SiteConfig {\n /** The title of the documentation site */\n title: string\n /** Short description of the documentation */\n description?: string\n /** URL where the documentation is hosted */\n url?: string\n /** Base URL path for the documentation (e.g., '/docs') */\n baseUrl?: string\n /** Path to the site logo */\n logo?: string\n /** Path to the favicon */\n favicon?: string\n /** Default language for the documentation */\n language?: string\n /** Organization or author name */\n organizationName?: string\n /** Project name */\n projectName?: string\n /** Active/default version for the documentation */\n activeVersion?: string\n}\n\n/**\n * Theme and appearance settings\n */\nexport interface ThemeConfig {\n /** Primary color for the theme */\n primaryColor?: string\n /** Default theme mode */\n defaultMode?: \"light\" | \"dark\" | \"system\"\n /** Whether to respect system preferences */\n respectPrefersColorScheme?: boolean\n /** Custom CSS file path */\n customCss?: string\n}\n\n/**\n * Tab group for organizing documentation\n */\nexport interface TabGroup {\n /** Unique identifier for the tab group */\n id: string\n /** Display label for the tab */\n label: string\n /** Optional icon name (lucide-react icon) */\n icon?: string\n}\n\n/**\n * Navigation and sidebar configuration\n */\nexport interface NavigationConfig {\n /** Whether to show the sidebar by default */\n showSidebar?: boolean\n /** Whether the sidebar is collapsible */\n collapsibleSidebar?: boolean\n /** Whether to show breadcrumbs */\n showBreadcrumbs?: boolean\n /** Whether to show table of contents */\n showTableOfContents?: boolean\n /** Position of table of contents */\n tocPosition?: \"left\" | \"right\"\n /** Maximum depth for table of contents */\n tocMaxDepth?: number\n /** Tab groups for organizing documentation sections */\n tabGroups?: TabGroup[]\n}\n\n/**\n * Social and external links\n */\nexport interface SocialLinks {\n /** GitHub repository URL */\n github?: string\n /** Twitter/X handle or URL */\n twitter?: string\n /** Discord invite URL */\n discord?: string\n /** LinkedIn profile or company page */\n linkedin?: string\n /** YouTube channel URL */\n youtube?: string\n /** Custom social links */\n custom?: Array<{\n label: string\n url: string\n icon?: string\n }>\n}\n\n/**\n * Search configuration\n */\nexport interface SearchConfig {\n /** Enable/disable search functionality */\n enabled?: boolean\n /** Placeholder text for search input */\n placeholder?: string\n /** Search provider type */\n provider?: \"meilisearch\" | \"algolia\" | \"local\"\n /** Meilisearch configuration */\n meilisearch?: {\n /** Meilisearch server URL */\n host: string\n /** API key for Meilisearch */\n apiKey?: string\n /** Index name */\n indexName: string\n }\n}\n\n/**\n * Analytics configuration\n */\nexport interface AnalyticsConfig {\n /** Google Analytics tracking ID */\n googleAnalytics?: string\n /** Google Tag Manager ID */\n googleTagManager?: string\n /** Plausible Analytics domain */\n plausible?: string\n /** Custom analytics scripts */\n custom?: Array<{\n src: string\n async?: boolean\n defer?: boolean\n }>\n}\n\n/**\n * Footer configuration\n */\nexport interface FooterConfig {\n /** Copyright text */\n copyright?: string\n /** Footer links organized by columns */\n links?: Array<{\n title: string\n items: Array<{\n label: string\n href: string\n }>\n }>\n /** Custom footer content */\n customContent?: string\n}\n\n/**\n * Documentation features\n */\nexport interface FeaturesConfig {\n /** Enable/disable edit this page links */\n editUrl?: string | false\n /** Show last updated timestamp */\n showLastUpdated?: boolean\n /** Show reading time estimate */\n showReadingTime?: boolean\n /** Show author information */\n showAuthors?: boolean\n /** Show tags */\n showTags?: boolean\n /** Enable version dropdown */\n versioning?: boolean\n /** Enable i18n (internationalization) */\n i18n?: boolean\n}\n\n/**\n * Site-wide banner configuration\n */\nexport interface BannerConfig {\n /** Whether the banner is enabled */\n enabled?: boolean\n /** Banner message */\n message?: string\n /** Banner type */\n type?: \"info\" | \"warning\" | \"success\" | \"error\"\n /** Whether the banner can be dismissed */\n dismissible?: boolean\n}\n\n/**\n * Environment variables that can be used in documentation\n * These will be replaced at build time or runtime\n */\nexport interface EnvironmentVariables {\n /** API base URL */\n API_BASE_URL?: string\n /** API version */\n API_VERSION?: string\n /** CDN URL */\n CDN_URL?: string\n /** Custom environment variables */\n [key: string]: string | undefined\n}\n\n/**\n * Deployment configuration for different hosting scenarios\n */\nexport interface DeploymentConfig {\n /**\n * Deployment target\n * - 'vercel': For Vercel or similar Node.js hosting (uses 'standalone' output)\n * - 'github-pages': For GitHub Pages static hosting (uses 'export' output)\n * - 'static': For any static hosting like Netlify, Cloudflare Pages, etc.\n * - 'custom-domain-static': For static hosting with custom domain (no basePath needed)\n */\n target?: \"vercel\" | \"github-pages\" | \"static\" | \"custom-domain-static\"\n\n /**\n * Base path for assets when deploying to GitHub Pages without custom domain\n * This should be your repository name (e.g., 'my-repo')\n * Only used when target is 'github-pages' and no custom domain is configured\n */\n basePath?: string\n\n /**\n * Whether a custom domain is configured\n * When true, basePath will be ignored even for GitHub Pages\n */\n customDomain?: boolean\n}\n\n/**\n * Main configuration interface\n */\nexport interface SpecraConfig {\n /** Site metadata and branding */\n site: SiteConfig\n /** Theme and appearance settings */\n theme?: ThemeConfig\n /** Navigation and sidebar configuration */\n navigation?: NavigationConfig\n /** Social and external links */\n social?: SocialLinks\n /** Search configuration */\n search?: SearchConfig\n /** Analytics configuration */\n analytics?: AnalyticsConfig\n /** Footer configuration */\n footer?: FooterConfig\n /** Site-wide banner */\n banner?: BannerConfig\n /** Documentation features */\n features?: FeaturesConfig\n /** Environment variables for use in docs */\n env?: EnvironmentVariables\n /** Deployment configuration */\n deployment?: DeploymentConfig\n}\n\n/**\n * Default configuration values\n */\nexport const defaultConfig: SpecraConfig = {\n site: {\n title: \"Documentation\",\n description: \"Project documentation\",\n baseUrl: \"/\",\n language: \"en\",\n },\n theme: {\n defaultMode: \"system\",\n respectPrefersColorScheme: true,\n },\n navigation: {\n showSidebar: true,\n collapsibleSidebar: true,\n showBreadcrumbs: true,\n showTableOfContents: true,\n tocPosition: \"right\",\n tocMaxDepth: 3,\n },\n search: {\n enabled: true,\n provider: \"local\",\n placeholder: \"Search documentation...\",\n },\n features: {\n showLastUpdated: true,\n showReadingTime: true,\n showAuthors: false,\n showTags: true,\n versioning: true,\n i18n: false,\n },\n}\n","import specraConfigJson from \"../specra.config.json\"\nimport { SpecraConfig, defaultConfig } from \"./config.types\"\n\n/**\n * Deep merge two objects\n */\nfunction deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T {\n const result = { ...target }\n\n for (const key in source) {\n const sourceValue = source[key]\n const targetValue = result[key]\n\n if (sourceValue && typeof sourceValue === \"object\" && !Array.isArray(sourceValue)) {\n result[key] = deepMerge(\n targetValue && typeof targetValue === \"object\" ? targetValue : {},\n sourceValue,\n ) as T[Extract<keyof T, string>]\n } else if (sourceValue !== undefined) {\n result[key] = sourceValue as T[Extract<keyof T, string>]\n }\n }\n\n return result\n}\n\n/**\n * Load and parse the Specra configuration file\n * Falls back to default configuration if file doesn't exist or is invalid\n */\nexport function loadConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n try {\n // const userConfig = specraConfigJson as unknown as Partial<SpecraConfig>\n\n // Merge user config with defaults \n const config = deepMerge(defaultConfig, userConfig)\n\n return config\n } catch (error) {\n console.error(`❌ Error loading configuration:`, error)\n console.warn(\"Using default configuration.\")\n return defaultConfig\n }\n}\n\n/**\n * Get a specific configuration value by path (SERVER ONLY)\n * Example: getConfigValue('site.title') or getConfigValue('theme.defaultMode')\n */\nexport function getConfigValue<T = any>(path: string, config?: SpecraConfig): T | undefined {\n const cfg = config || loadConfig({})\n const keys = path.split(\".\")\n let value: any = cfg\n\n for (const key of keys) {\n if (value && typeof value === \"object\" && key in value) {\n value = value[key]\n } else {\n return undefined\n }\n }\n\n return value as T\n}\n\n/**\n * Replace environment variables in a string (SERVER ONLY)\n * Supports ${ENV_VAR} and {{ENV_VAR}} syntax\n */\nexport function replaceEnvVariables(text: string, config?: SpecraConfig): string {\n const cfg = config || loadConfig({})\n const envVars = cfg.env || {}\n\n let result = text\n\n // Replace ${VAR} syntax\n result = result.replace(/\\$\\{([^}]+)\\}/g, (match, varName) => {\n return envVars[varName] || match\n })\n\n // Replace {{VAR}} syntax\n result = result.replace(/\\{\\{([^}]+)\\}\\}/g, (match, varName) => {\n return envVars[varName] || match\n })\n\n return result\n}\n\n/**\n * Process content and replace all environment variables (SERVER ONLY)\n */\nexport function processContentWithEnv(content: string, config?: SpecraConfig): string {\n return replaceEnvVariables(content, config)\n}\n\n/**\n * Validate configuration (basic validation) (SERVER ONLY)\n */\nexport function validateConfig(config: SpecraConfig): { valid: boolean; errors: string[] } {\n const errors: string[] = []\n\n // Required fields\n if (!config.site?.title) {\n errors.push(\"site.title is required\")\n }\n\n // URL validation\n if (config.site?.url) {\n try {\n new URL(config.site.url)\n } catch {\n errors.push(\"site.url must be a valid URL\")\n }\n }\n\n // Social links validation\n if (config.social) {\n const socialKeys = [\"github\", \"twitter\", \"discord\", \"linkedin\", \"youtube\"] as const\n for (const key of socialKeys) {\n const url = config.social[key]\n if (url) {\n try {\n new URL(url)\n } catch {\n errors.push(`social.${key} must be a valid URL`)\n }\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n }\n}\n\n// Singleton instance\nlet configInstance: SpecraConfig | null = null\n\n/**\n * Initialize the Specra configuration\n * Can be called multiple times - subsequent calls will update the config\n * @param userConfig - Partial configuration to merge with defaults\n * @returns The initialized configuration\n */\nexport function initConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n configInstance = loadConfig(userConfig)\n return configInstance\n}\n\n/**\n * Get the configuration instance (cached) (SERVER ONLY)\n * If not initialized, returns default config\n */\nexport function getConfig(): SpecraConfig {\n if (!configInstance) {\n // Auto-initialize with defaults if not already initialized\n configInstance = loadConfig({})\n }\n return configInstance\n}\n\n/**\n * Reload the configuration (useful for development) (SERVER ONLY)\n */\nexport function reloadConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {\n configInstance = loadConfig(userConfig)\n return configInstance\n}\n\n/**\n * Export the loaded config as default (SERVER ONLY)\n */\n// export default getConfig()\n","import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n\n\n/**\n * Get the correct asset path based on deployment configuration\n * Handles different deployment scenarios:\n * - Vercel/Node.js hosting (standalone build): No basePath needed\n * - GitHub Pages without custom domain: Uses basePath from config\n * - Static hosting with custom domain: No basePath needed\n *\n * @param path - The asset path (can start with or without '/')\n * @returns The properly formatted asset path\n */\nexport function getAssetPath(path: string): string {\n // Get basePath from Next.js config (set during build for static exports)\n const basePath = process.env.NEXT_PUBLIC_BASE_PATH || process.env.__NEXT_ROUTER_BASEPATH || ''\n\n // Normalize the input path: ensure it starts with '/'\n const normalizedPath = path.startsWith('/') ? path : `/${path}`\n\n // If we have a basePath (GitHub Pages without custom domain), prepend it\n if (basePath) {\n // Normalize basePath: remove trailing slash, ensure leading slash\n const normalizedBase = basePath.startsWith('/') ? basePath : `/${basePath}`\n const cleanBase = normalizedBase.replace(/\\/$/, '')\n return `${cleanBase}${normalizedPath}`\n }\n\n // Default: return the normalized path (works for Vercel, custom domains, and dev)\n return normalizedPath\n}"],"mappings":"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","names":[]}
@@ -448,9 +448,9 @@ interface HeaderProps {
448
448
  currentVersion: string;
449
449
  versions: string[];
450
450
  onMenuClick?: () => void;
451
- config: SpecraConfig;
451
+ config?: SpecraConfig;
452
452
  }
453
- declare function Header({ currentVersion, versions, onMenuClick, config }: HeaderProps): react_jsx_runtime.JSX.Element;
453
+ declare function Header({ currentVersion, versions, onMenuClick, config: configProp }: HeaderProps): react_jsx_runtime.JSX.Element;
454
454
 
455
455
  declare function HotReloadIndicator(): react_jsx_runtime.JSX.Element | null;
456
456
 
@@ -785,6 +785,42 @@ interface VersionSwitcherProps {
785
785
  }
786
786
  declare function VersionSwitcher({ currentVersion, versions }: VersionSwitcherProps): react_jsx_runtime.JSX.Element;
787
787
 
788
+ interface ConfigProviderProps {
789
+ config: SpecraConfig;
790
+ children: React$1.ReactNode;
791
+ }
792
+ /**
793
+ * Provider component that makes Specra config available to all client components
794
+ * Usage: Wrap your app with this provider in your root layout
795
+ */
796
+ declare function ConfigProvider({ config, children }: ConfigProviderProps): react_jsx_runtime.JSX.Element;
797
+ /**
798
+ * Hook to access Specra configuration in any client component
799
+ * @returns The current Specra configuration
800
+ * @example
801
+ * ```tsx
802
+ * function MyComponent() {
803
+ * const config = useConfig()
804
+ * return <div>{config.site.title}</div>
805
+ * }
806
+ * ```
807
+ */
808
+ declare function useConfig(): SpecraConfig;
809
+ /**
810
+ * Hook to access a specific configuration value by path
811
+ * @param path - Dot-separated path to the config value (e.g., "site.title")
812
+ * @returns The configuration value at the specified path
813
+ * @example
814
+ * ```tsx
815
+ * function MyComponent() {
816
+ * const title = useConfigValue<string>("site.title")
817
+ * const showSidebar = useConfigValue<boolean>("navigation.showSidebar")
818
+ * return <div>{title}</div>
819
+ * }
820
+ * ```
821
+ */
822
+ declare function useConfigValue<T = any>(path: string): T | undefined;
823
+
788
824
  declare const badgeVariants: (props?: ({
789
825
  variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
790
826
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -819,4 +855,4 @@ declare function Textarea({ className, ...props }: React$1.ComponentProps<"texta
819
855
 
820
856
  declare function VersionNotFound(): react_jsx_runtime.JSX.Element;
821
857
 
822
- export { Accordion, AccordionItem, ApiEndpoint, ApiParams, ApiPlayground, ApiReference, ApiResponse, ApiResponse as ApiResponseDisplay, Badge, Breadcrumb, Button, COMPONENT_TEXT_PROPS, Callout, Card, CardGrid, CategoryIndex, CodeBlock, Column, Columns, DevModeBadge, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Badge$1 as DocBadge, DocLayout, DocLayoutWrapper, DocLoading, DocMetadata, DocNavigation, DocTags, DraftBadge, Footer, Frame, Header, HotReloadIndicator, Icon, Image, ImageCard, ImageCardGrid, Input, Math, MdxHotReload, Mermaid, MobileDocLayout, NotFoundContent, SearchHighlight, SearchModal, Sidebar, SidebarSkeleton, SiteBanner, Step, Steps, Tab, TabGroups, TabProvider, TableOfContents, Tabs, Textarea, ThemeToggle, Tooltip, VersionNotFound, VersionSwitcher, Video, badgeVariants, buttonVariants, extractComponentPropsText, extractSearchText, mdxComponents, useTabContext };
858
+ export { Accordion, AccordionItem, ApiEndpoint, ApiParams, ApiPlayground, ApiReference, ApiResponse, ApiResponse as ApiResponseDisplay, Badge, Breadcrumb, Button, COMPONENT_TEXT_PROPS, Callout, Card, CardGrid, CategoryIndex, CodeBlock, Column, Columns, ConfigProvider, DevModeBadge, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Badge$1 as DocBadge, DocLayout, DocLayoutWrapper, DocLoading, DocMetadata, DocNavigation, DocTags, DraftBadge, Footer, Frame, Header, HotReloadIndicator, Icon, Image, ImageCard, ImageCardGrid, Input, Math, MdxHotReload, Mermaid, MobileDocLayout, NotFoundContent, SearchHighlight, SearchModal, Sidebar, SidebarSkeleton, SiteBanner, Step, Steps, Tab, TabGroups, TabProvider, TableOfContents, Tabs, Textarea, ThemeToggle, Tooltip, VersionNotFound, VersionSwitcher, Video, badgeVariants, buttonVariants, extractComponentPropsText, extractSearchText, mdxComponents, useConfig, useConfigValue, useTabContext };
@@ -448,9 +448,9 @@ interface HeaderProps {
448
448
  currentVersion: string;
449
449
  versions: string[];
450
450
  onMenuClick?: () => void;
451
- config: SpecraConfig;
451
+ config?: SpecraConfig;
452
452
  }
453
- declare function Header({ currentVersion, versions, onMenuClick, config }: HeaderProps): react_jsx_runtime.JSX.Element;
453
+ declare function Header({ currentVersion, versions, onMenuClick, config: configProp }: HeaderProps): react_jsx_runtime.JSX.Element;
454
454
 
455
455
  declare function HotReloadIndicator(): react_jsx_runtime.JSX.Element | null;
456
456
 
@@ -785,6 +785,42 @@ interface VersionSwitcherProps {
785
785
  }
786
786
  declare function VersionSwitcher({ currentVersion, versions }: VersionSwitcherProps): react_jsx_runtime.JSX.Element;
787
787
 
788
+ interface ConfigProviderProps {
789
+ config: SpecraConfig;
790
+ children: React$1.ReactNode;
791
+ }
792
+ /**
793
+ * Provider component that makes Specra config available to all client components
794
+ * Usage: Wrap your app with this provider in your root layout
795
+ */
796
+ declare function ConfigProvider({ config, children }: ConfigProviderProps): react_jsx_runtime.JSX.Element;
797
+ /**
798
+ * Hook to access Specra configuration in any client component
799
+ * @returns The current Specra configuration
800
+ * @example
801
+ * ```tsx
802
+ * function MyComponent() {
803
+ * const config = useConfig()
804
+ * return <div>{config.site.title}</div>
805
+ * }
806
+ * ```
807
+ */
808
+ declare function useConfig(): SpecraConfig;
809
+ /**
810
+ * Hook to access a specific configuration value by path
811
+ * @param path - Dot-separated path to the config value (e.g., "site.title")
812
+ * @returns The configuration value at the specified path
813
+ * @example
814
+ * ```tsx
815
+ * function MyComponent() {
816
+ * const title = useConfigValue<string>("site.title")
817
+ * const showSidebar = useConfigValue<boolean>("navigation.showSidebar")
818
+ * return <div>{title}</div>
819
+ * }
820
+ * ```
821
+ */
822
+ declare function useConfigValue<T = any>(path: string): T | undefined;
823
+
788
824
  declare const badgeVariants: (props?: ({
789
825
  variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
790
826
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -819,4 +855,4 @@ declare function Textarea({ className, ...props }: React$1.ComponentProps<"texta
819
855
 
820
856
  declare function VersionNotFound(): react_jsx_runtime.JSX.Element;
821
857
 
822
- export { Accordion, AccordionItem, ApiEndpoint, ApiParams, ApiPlayground, ApiReference, ApiResponse, ApiResponse as ApiResponseDisplay, Badge, Breadcrumb, Button, COMPONENT_TEXT_PROPS, Callout, Card, CardGrid, CategoryIndex, CodeBlock, Column, Columns, DevModeBadge, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Badge$1 as DocBadge, DocLayout, DocLayoutWrapper, DocLoading, DocMetadata, DocNavigation, DocTags, DraftBadge, Footer, Frame, Header, HotReloadIndicator, Icon, Image, ImageCard, ImageCardGrid, Input, Math, MdxHotReload, Mermaid, MobileDocLayout, NotFoundContent, SearchHighlight, SearchModal, Sidebar, SidebarSkeleton, SiteBanner, Step, Steps, Tab, TabGroups, TabProvider, TableOfContents, Tabs, Textarea, ThemeToggle, Tooltip, VersionNotFound, VersionSwitcher, Video, badgeVariants, buttonVariants, extractComponentPropsText, extractSearchText, mdxComponents, useTabContext };
858
+ export { Accordion, AccordionItem, ApiEndpoint, ApiParams, ApiPlayground, ApiReference, ApiResponse, ApiResponse as ApiResponseDisplay, Badge, Breadcrumb, Button, COMPONENT_TEXT_PROPS, Callout, Card, CardGrid, CategoryIndex, CodeBlock, Column, Columns, ConfigProvider, DevModeBadge, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Badge$1 as DocBadge, DocLayout, DocLayoutWrapper, DocLoading, DocMetadata, DocNavigation, DocTags, DraftBadge, Footer, Frame, Header, HotReloadIndicator, Icon, Image, ImageCard, ImageCardGrid, Input, Math, MdxHotReload, Mermaid, MobileDocLayout, NotFoundContent, SearchHighlight, SearchModal, Sidebar, SidebarSkeleton, SiteBanner, Step, Steps, Tab, TabGroups, TabProvider, TableOfContents, Tabs, Textarea, ThemeToggle, Tooltip, VersionNotFound, VersionSwitcher, Video, badgeVariants, buttonVariants, extractComponentPropsText, extractSearchText, mdxComponents, useConfig, useConfigValue, useTabContext };