@usecross/docs 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ssr.js CHANGED
@@ -3,10 +3,21 @@ import { createInertiaApp } from "@inertiajs/react";
3
3
  import createServer from "@inertiajs/react/server";
4
4
  import ReactDOMServer from "react-dom/server";
5
5
 
6
- // src/components/ThemeProvider.tsx
7
- import { createContext, useContext, useEffect, useState } from "react";
6
+ // src/context/ComponentsContext.tsx
7
+ import { createContext, useContext } from "react";
8
8
  import { jsx } from "react/jsx-runtime";
9
- var ThemeContext = createContext(null);
9
+ var ComponentsContext = createContext({});
10
+ function ComponentsProvider({
11
+ children,
12
+ components
13
+ }) {
14
+ return /* @__PURE__ */ jsx(ComponentsContext.Provider, { value: { components }, children });
15
+ }
16
+
17
+ // src/components/ThemeProvider.tsx
18
+ import { createContext as createContext2, useContext as useContext2, useEffect, useState } from "react";
19
+ import { jsx as jsx2 } from "react/jsx-runtime";
20
+ var ThemeContext = createContext2(null);
10
21
  var STORAGE_KEY = "cross-docs-theme";
11
22
  function getSystemTheme() {
12
23
  if (typeof window === "undefined") return "light";
@@ -66,7 +77,7 @@ function ThemeProvider({
66
77
  setThemeState(newTheme);
67
78
  localStorage.setItem(STORAGE_KEY, newTheme);
68
79
  };
69
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { theme, resolvedTheme, setTheme }, children });
80
+ return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value: { theme, resolvedTheme, setTheme }, children });
70
81
  }
71
82
  var themeInitScript = `
72
83
  (function() {
@@ -80,9 +91,9 @@ var themeInitScript = `
80
91
  `.trim();
81
92
 
82
93
  // src/ssr.tsx
83
- import { jsx as jsx2 } from "react/jsx-runtime";
94
+ import { jsx as jsx3 } from "react/jsx-runtime";
84
95
  function createDocsServer(config) {
85
- const { pages, title } = config;
96
+ const { pages, title, components } = config;
86
97
  createServer(
87
98
  (page) => createInertiaApp({
88
99
  page,
@@ -95,7 +106,7 @@ function createDocsServer(config) {
95
106
  }
96
107
  return pageComponent;
97
108
  },
98
- setup: ({ App, props }) => /* @__PURE__ */ jsx2(ThemeProvider, { children: /* @__PURE__ */ jsx2(App, { ...props }) })
109
+ setup: ({ App, props }) => /* @__PURE__ */ jsx3(ThemeProvider, { children: /* @__PURE__ */ jsx3(ComponentsProvider, { components, children: /* @__PURE__ */ jsx3(App, { ...props }) }) })
99
110
  })
100
111
  );
101
112
  }
package/dist/ssr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ssr.tsx","../src/components/ThemeProvider.tsx"],"sourcesContent":["import { createInertiaApp } from '@inertiajs/react'\nimport createServer from '@inertiajs/react/server'\nimport ReactDOMServer from 'react-dom/server'\nimport type { DocsAppConfig } from './types'\nimport { ThemeProvider } from './components/ThemeProvider'\n\n/**\n * Create an SSR server for documentation.\n *\n * @example\n * ```tsx\n * import { createDocsServer, DocsPage } from '@usecross/docs'\n *\n * createDocsServer({\n * pages: {\n * 'docs/DocsPage': DocsPage,\n * },\n * title: (title) => `${title} - My Docs`,\n * })\n * ```\n */\nexport function createDocsServer(config: DocsAppConfig): void {\n const { pages, title } = config\n\n createServer((page) =>\n createInertiaApp({\n page,\n render: ReactDOMServer.renderToString,\n title: title ?? ((pageTitle) => (pageTitle ? `${pageTitle}` : 'Documentation')),\n resolve: (name) => {\n const pageComponent = pages[name]\n if (!pageComponent) {\n throw new Error(`Page component \"${name}\" not found`)\n }\n return pageComponent\n },\n setup: ({ App, props }) => (\n <ThemeProvider>\n <App {...props} />\n </ThemeProvider>\n ),\n })\n )\n}\n","import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'\n\nexport type Theme = 'light' | 'dark' | 'system'\nexport type ResolvedTheme = 'light' | 'dark'\n\ninterface ThemeContextValue {\n theme: Theme\n resolvedTheme: ResolvedTheme\n setTheme: (theme: Theme) => void\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null)\n\nconst STORAGE_KEY = 'cross-docs-theme'\n\nfunction getSystemTheme(): ResolvedTheme {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getStoredTheme(): Theme | null {\n if (typeof window === 'undefined') return null\n const stored = localStorage.getItem(STORAGE_KEY)\n if (stored === 'light' || stored === 'dark' || stored === 'system') {\n return stored\n }\n return null\n}\n\ninterface ThemeProviderProps {\n children: ReactNode\n /** Default theme if no preference is stored. Defaults to 'system'. */\n defaultTheme?: Theme\n /** Force a specific theme, ignoring user preference */\n forcedTheme?: ResolvedTheme\n}\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n forcedTheme,\n}: ThemeProviderProps) {\n const [theme, setThemeState] = useState<Theme>(() => {\n // During SSR, use defaultTheme\n if (typeof window === 'undefined') return defaultTheme\n return getStoredTheme() ?? defaultTheme\n })\n\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => {\n if (forcedTheme) return forcedTheme\n if (typeof window === 'undefined') return 'light'\n if (theme === 'system') return getSystemTheme()\n return theme\n })\n\n // Update resolved theme when theme changes or system preference changes\n useEffect(() => {\n if (forcedTheme) {\n setResolvedTheme(forcedTheme)\n return\n }\n\n const updateResolvedTheme = () => {\n if (theme === 'system') {\n setResolvedTheme(getSystemTheme())\n } else {\n setResolvedTheme(theme)\n }\n }\n\n updateResolvedTheme()\n\n // Listen for system preference changes\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n const handleChange = () => {\n if (theme === 'system') {\n setResolvedTheme(getSystemTheme())\n }\n }\n\n mediaQuery.addEventListener('change', handleChange)\n return () => mediaQuery.removeEventListener('change', handleChange)\n }, [theme, forcedTheme])\n\n // Apply theme class to document\n useEffect(() => {\n const root = document.documentElement\n root.classList.remove('light', 'dark')\n root.classList.add(resolvedTheme)\n }, [resolvedTheme])\n\n const setTheme = (newTheme: Theme) => {\n setThemeState(newTheme)\n localStorage.setItem(STORAGE_KEY, newTheme)\n }\n\n return (\n <ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}>\n {children}\n </ThemeContext.Provider>\n )\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = useContext(ThemeContext)\n if (!context) {\n throw new Error('useTheme must be used within a ThemeProvider')\n }\n return context\n}\n\n/**\n * Script to prevent flash of unstyled content (FOUC) during page load.\n * Include this in your HTML <head> before any stylesheets.\n */\nexport const themeInitScript = `\n(function() {\n try {\n var stored = localStorage.getItem('${STORAGE_KEY}');\n var theme = stored === 'light' || stored === 'dark' ? stored :\n (stored === 'system' || !stored) && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n document.documentElement.classList.add(theme);\n } catch (e) {}\n})();\n`.trim()\n"],"mappings":";AAAA,SAAS,wBAAwB;AACjC,OAAO,kBAAkB;AACzB,OAAO,oBAAoB;;;ACF3B,SAAS,eAAe,YAAY,WAAW,gBAAgC;AAiG3E;AAtFJ,IAAM,eAAe,cAAwC,IAAI;AAEjE,IAAM,cAAc;AAEpB,SAAS,iBAAgC;AACvC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO,WAAW,8BAA8B,EAAE,UAAU,SAAS;AAC9E;AAEA,SAAS,iBAA+B;AACtC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAM,SAAS,aAAa,QAAQ,WAAW;AAC/C,MAAI,WAAW,WAAW,WAAW,UAAU,WAAW,UAAU;AAClE,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAUO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,eAAe;AAAA,EACf;AACF,GAAuB;AACrB,QAAM,CAAC,OAAO,aAAa,IAAI,SAAgB,MAAM;AAEnD,QAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,WAAO,eAAe,KAAK;AAAA,EAC7B,CAAC;AAED,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwB,MAAM;AACtE,QAAI,YAAa,QAAO;AACxB,QAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,QAAI,UAAU,SAAU,QAAO,eAAe;AAC9C,WAAO;AAAA,EACT,CAAC;AAGD,YAAU,MAAM;AACd,QAAI,aAAa;AACf,uBAAiB,WAAW;AAC5B;AAAA,IACF;AAEA,UAAM,sBAAsB,MAAM;AAChC,UAAI,UAAU,UAAU;AACtB,yBAAiB,eAAe,CAAC;AAAA,MACnC,OAAO;AACL,yBAAiB,KAAK;AAAA,MACxB;AAAA,IACF;AAEA,wBAAoB;AAGpB,UAAM,aAAa,OAAO,WAAW,8BAA8B;AACnE,UAAM,eAAe,MAAM;AACzB,UAAI,UAAU,UAAU;AACtB,yBAAiB,eAAe,CAAC;AAAA,MACnC;AAAA,IACF;AAEA,eAAW,iBAAiB,UAAU,YAAY;AAClD,WAAO,MAAM,WAAW,oBAAoB,UAAU,YAAY;AAAA,EACpE,GAAG,CAAC,OAAO,WAAW,CAAC;AAGvB,YAAU,MAAM;AACd,UAAM,OAAO,SAAS;AACtB,SAAK,UAAU,OAAO,SAAS,MAAM;AACrC,SAAK,UAAU,IAAI,aAAa;AAAA,EAClC,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,WAAW,CAAC,aAAoB;AACpC,kBAAc,QAAQ;AACtB,iBAAa,QAAQ,aAAa,QAAQ;AAAA,EAC5C;AAEA,SACE,oBAAC,aAAa,UAAb,EAAsB,OAAO,EAAE,OAAO,eAAe,SAAS,GAC5D,UACH;AAEJ;AAcO,IAAM,kBAAkB;AAAA;AAAA;AAAA,yCAGU,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMlD,KAAK;;;ADtFG,gBAAAA,YAAA;AAjBH,SAAS,iBAAiB,QAA6B;AAC5D,QAAM,EAAE,OAAO,MAAM,IAAI;AAEzB;AAAA,IAAa,CAAC,SACZ,iBAAiB;AAAA,MACf;AAAA,MACA,QAAQ,eAAe;AAAA,MACvB,OAAO,UAAU,CAAC,cAAe,YAAY,GAAG,SAAS,KAAK;AAAA,MAC9D,SAAS,CAAC,SAAS;AACjB,cAAM,gBAAgB,MAAM,IAAI;AAChC,YAAI,CAAC,eAAe;AAClB,gBAAM,IAAI,MAAM,mBAAmB,IAAI,aAAa;AAAA,QACtD;AACA,eAAO;AAAA,MACT;AAAA,MACA,OAAO,CAAC,EAAE,KAAK,MAAM,MACnB,gBAAAA,KAAC,iBACC,0BAAAA,KAAC,OAAK,GAAG,OAAO,GAClB;AAAA,IAEJ,CAAC;AAAA,EACH;AACF;","names":["jsx"]}
1
+ {"version":3,"sources":["../src/ssr.tsx","../src/context/ComponentsContext.tsx","../src/components/ThemeProvider.tsx"],"sourcesContent":["import { createInertiaApp } from '@inertiajs/react'\nimport createServer from '@inertiajs/react/server'\nimport ReactDOMServer from 'react-dom/server'\nimport type { DocsAppConfig } from './types'\nimport { ComponentsProvider } from './context/ComponentsContext'\nimport { ThemeProvider } from './components/ThemeProvider'\n\n/**\n * Create an SSR server for documentation.\n *\n * @example\n * ```tsx\n * import { createDocsServer, DocsPage } from '@usecross/docs'\n *\n * createDocsServer({\n * pages: {\n * 'docs/DocsPage': DocsPage,\n * },\n * title: (title) => `${title} - My Docs`,\n * })\n * ```\n */\nexport function createDocsServer(config: DocsAppConfig): void {\n const { pages, title, components } = config\n\n createServer((page) =>\n createInertiaApp({\n page,\n render: ReactDOMServer.renderToString,\n title: title ?? ((pageTitle) => (pageTitle ? `${pageTitle}` : 'Documentation')),\n resolve: (name) => {\n const pageComponent = pages[name]\n if (!pageComponent) {\n throw new Error(`Page component \"${name}\" not found`)\n }\n return pageComponent\n },\n setup: ({ App, props }) => (\n <ThemeProvider>\n <ComponentsProvider components={components}>\n <App {...props} />\n </ComponentsProvider>\n </ThemeProvider>\n ),\n })\n )\n}\n","import React, { createContext, useContext } from 'react'\n\ninterface ComponentsContextValue {\n components?: Record<string, React.ComponentType<any>>\n}\n\nconst ComponentsContext = createContext<ComponentsContextValue>({})\n\nexport function ComponentsProvider({\n children,\n components,\n}: {\n children: React.ReactNode\n components?: Record<string, React.ComponentType<any>>\n}) {\n return (\n <ComponentsContext.Provider value={{ components }}>\n {children}\n </ComponentsContext.Provider>\n )\n}\n\nexport function useComponents() {\n return useContext(ComponentsContext)\n}\n","import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'\n\nexport type Theme = 'light' | 'dark' | 'system'\nexport type ResolvedTheme = 'light' | 'dark'\n\ninterface ThemeContextValue {\n theme: Theme\n resolvedTheme: ResolvedTheme\n setTheme: (theme: Theme) => void\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null)\n\nconst STORAGE_KEY = 'cross-docs-theme'\n\nfunction getSystemTheme(): ResolvedTheme {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getStoredTheme(): Theme | null {\n if (typeof window === 'undefined') return null\n const stored = localStorage.getItem(STORAGE_KEY)\n if (stored === 'light' || stored === 'dark' || stored === 'system') {\n return stored\n }\n return null\n}\n\ninterface ThemeProviderProps {\n children: ReactNode\n /** Default theme if no preference is stored. Defaults to 'system'. */\n defaultTheme?: Theme\n /** Force a specific theme, ignoring user preference */\n forcedTheme?: ResolvedTheme\n}\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n forcedTheme,\n}: ThemeProviderProps) {\n const [theme, setThemeState] = useState<Theme>(() => {\n // During SSR, use defaultTheme\n if (typeof window === 'undefined') return defaultTheme\n return getStoredTheme() ?? defaultTheme\n })\n\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => {\n if (forcedTheme) return forcedTheme\n if (typeof window === 'undefined') return 'light'\n if (theme === 'system') return getSystemTheme()\n return theme\n })\n\n // Update resolved theme when theme changes or system preference changes\n useEffect(() => {\n if (forcedTheme) {\n setResolvedTheme(forcedTheme)\n return\n }\n\n const updateResolvedTheme = () => {\n if (theme === 'system') {\n setResolvedTheme(getSystemTheme())\n } else {\n setResolvedTheme(theme)\n }\n }\n\n updateResolvedTheme()\n\n // Listen for system preference changes\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n const handleChange = () => {\n if (theme === 'system') {\n setResolvedTheme(getSystemTheme())\n }\n }\n\n mediaQuery.addEventListener('change', handleChange)\n return () => mediaQuery.removeEventListener('change', handleChange)\n }, [theme, forcedTheme])\n\n // Apply theme class to document\n useEffect(() => {\n const root = document.documentElement\n root.classList.remove('light', 'dark')\n root.classList.add(resolvedTheme)\n }, [resolvedTheme])\n\n const setTheme = (newTheme: Theme) => {\n setThemeState(newTheme)\n localStorage.setItem(STORAGE_KEY, newTheme)\n }\n\n return (\n <ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}>\n {children}\n </ThemeContext.Provider>\n )\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = useContext(ThemeContext)\n if (!context) {\n // During SSR, the context may not be available due to module\n // duplication in the bundle. Return safe defaults instead of throwing.\n if (typeof window === 'undefined') {\n return { theme: 'system', resolvedTheme: 'light', setTheme: () => {} }\n }\n throw new Error('useTheme must be used within a ThemeProvider')\n }\n return context\n}\n\n/**\n * Script to prevent flash of unstyled content (FOUC) during page load.\n * Include this in your HTML <head> before any stylesheets.\n */\nexport const themeInitScript = `\n(function() {\n try {\n var stored = localStorage.getItem('${STORAGE_KEY}');\n var theme = stored === 'light' || stored === 'dark' ? stored :\n (stored === 'system' || !stored) && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n document.documentElement.classList.add(theme);\n } catch (e) {}\n})();\n`.trim()\n"],"mappings":"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","names":["createContext","useContext","jsx","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usecross/docs",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "description": "Documentation framework built on Cross-Inertia",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -104,6 +104,11 @@ export function ThemeProvider({
104
104
  export function useTheme(): ThemeContextValue {
105
105
  const context = useContext(ThemeContext)
106
106
  if (!context) {
107
+ // During SSR, the context may not be available due to module
108
+ // duplication in the bundle. Return safe defaults instead of throwing.
109
+ if (typeof window === 'undefined') {
110
+ return { theme: 'system', resolvedTheme: 'light', setTheme: () => {} }
111
+ }
107
112
  throw new Error('useTheme must be used within a ThemeProvider')
108
113
  }
109
114
  return context
package/src/ssr.tsx CHANGED
@@ -2,6 +2,7 @@ import { createInertiaApp } from '@inertiajs/react'
2
2
  import createServer from '@inertiajs/react/server'
3
3
  import ReactDOMServer from 'react-dom/server'
4
4
  import type { DocsAppConfig } from './types'
5
+ import { ComponentsProvider } from './context/ComponentsContext'
5
6
  import { ThemeProvider } from './components/ThemeProvider'
6
7
 
7
8
  /**
@@ -20,7 +21,7 @@ import { ThemeProvider } from './components/ThemeProvider'
20
21
  * ```
21
22
  */
22
23
  export function createDocsServer(config: DocsAppConfig): void {
23
- const { pages, title } = config
24
+ const { pages, title, components } = config
24
25
 
25
26
  createServer((page) =>
26
27
  createInertiaApp({
@@ -36,7 +37,9 @@ export function createDocsServer(config: DocsAppConfig): void {
36
37
  },
37
38
  setup: ({ App, props }) => (
38
39
  <ThemeProvider>
39
- <App {...props} />
40
+ <ComponentsProvider components={components}>
41
+ <App {...props} />
42
+ </ComponentsProvider>
40
43
  </ThemeProvider>
41
44
  ),
42
45
  })