boltdocs 2.5.6 → 2.6.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 (112) hide show
  1. package/bin/boltdocs.js +2 -2
  2. package/dist/client/index.cjs +6 -0
  3. package/dist/client/{index.d.mts → index.d.cts} +134 -252
  4. package/dist/client/index.d.ts +135 -253
  5. package/dist/client/index.js +1 -1
  6. package/dist/client/theme/neutral.css +90 -50
  7. package/dist/node/cli-entry.cjs +2 -2
  8. package/dist/node/cli-entry.mjs +2 -2
  9. package/dist/node/index.cjs +1 -1
  10. package/dist/node/index.d.cts +150 -205
  11. package/dist/node/index.d.mts +150 -205
  12. package/dist/node/index.mjs +1 -1
  13. package/dist/node-BgvNl2Ay.mjs +89 -0
  14. package/dist/node-vkbb0MK7.cjs +89 -0
  15. package/dist/{package-OFZf0s2j.mjs → package-CR0HF9x3.mjs} +1 -1
  16. package/dist/{package-BY8Jd2j4.cjs → package-Dgmsc_l5.cjs} +1 -1
  17. package/dist/search-dialog-3lvKsbVG.js +6 -0
  18. package/dist/search-dialog-DMK5OpgH.cjs +6 -0
  19. package/dist/use-search-C9bxCqfF.js +6 -0
  20. package/dist/use-search-DcfZSunO.cjs +6 -0
  21. package/package.json +19 -22
  22. package/src/client/app/config-context.tsx +38 -5
  23. package/src/client/app/doc-page.tsx +34 -0
  24. package/src/client/app/mdx-component.tsx +2 -3
  25. package/src/client/app/mdx-components-context.tsx +27 -2
  26. package/src/client/app/routes-context.tsx +34 -0
  27. package/src/client/app/scroll-handler.tsx +7 -4
  28. package/src/client/app/theme-context.tsx +71 -67
  29. package/src/client/components/default-layout.tsx +13 -14
  30. package/src/client/components/docs-layout.tsx +1 -2
  31. package/src/client/components/icons-dev.tsx +36 -5
  32. package/src/client/components/mdx/admonition.tsx +11 -27
  33. package/src/client/components/mdx/badge.tsx +1 -1
  34. package/src/client/components/mdx/button.tsx +3 -3
  35. package/src/client/components/mdx/card.tsx +1 -1
  36. package/src/client/components/mdx/code-block.tsx +90 -80
  37. package/src/client/components/mdx/component-preview.tsx +1 -5
  38. package/src/client/components/mdx/component-props.tsx +1 -1
  39. package/src/client/components/mdx/field.tsx +4 -5
  40. package/src/client/components/mdx/file-tree.tsx +6 -3
  41. package/src/client/components/mdx/hooks/use-code-block.ts +2 -2
  42. package/src/client/components/mdx/image.tsx +1 -1
  43. package/src/client/components/mdx/link.tsx +2 -2
  44. package/src/client/components/mdx/list.tsx +1 -1
  45. package/src/client/components/mdx/table.tsx +1 -1
  46. package/src/client/components/mdx/tabs.tsx +1 -1
  47. package/src/client/components/primitives/breadcrumbs.tsx +1 -7
  48. package/src/client/components/primitives/button-group.tsx +1 -1
  49. package/src/client/components/primitives/button.tsx +1 -1
  50. package/src/client/components/primitives/code-block.tsx +113 -0
  51. package/src/client/components/primitives/link.tsx +23 -41
  52. package/src/client/components/primitives/menu.tsx +5 -6
  53. package/src/client/components/primitives/navbar.tsx +6 -18
  54. package/src/client/components/primitives/navigation-menu.tsx +4 -4
  55. package/src/client/components/primitives/on-this-page.tsx +6 -10
  56. package/src/client/components/primitives/page-nav.tsx +4 -9
  57. package/src/client/components/primitives/popover.tsx +1 -1
  58. package/src/client/components/primitives/search-dialog.tsx +3 -6
  59. package/src/client/components/primitives/sidebar.tsx +80 -22
  60. package/src/client/components/primitives/skeleton.tsx +1 -1
  61. package/src/client/components/primitives/tabs.tsx +4 -11
  62. package/src/client/components/primitives/tooltip.tsx +3 -3
  63. package/src/client/components/ui-base/breadcrumbs.tsx +4 -6
  64. package/src/client/components/ui-base/copy-markdown.tsx +2 -7
  65. package/src/client/components/ui-base/github-stars.tsx +2 -2
  66. package/src/client/components/ui-base/head.tsx +58 -51
  67. package/src/client/components/ui-base/loading.tsx +2 -2
  68. package/src/client/components/ui-base/navbar.tsx +12 -14
  69. package/src/client/components/ui-base/not-found.tsx +1 -1
  70. package/src/client/components/ui-base/on-this-page.tsx +6 -6
  71. package/src/client/components/ui-base/page-nav.tsx +4 -8
  72. package/src/client/components/ui-base/search-dialog.tsx +10 -8
  73. package/src/client/components/ui-base/sidebar.tsx +76 -23
  74. package/src/client/components/ui-base/tabs.tsx +9 -8
  75. package/src/client/components/ui-base/theme-toggle.tsx +2 -2
  76. package/src/client/hooks/use-i18n.ts +3 -3
  77. package/src/client/hooks/use-localized-to.ts +1 -1
  78. package/src/client/hooks/use-navbar.ts +8 -6
  79. package/src/client/hooks/use-routes.ts +19 -11
  80. package/src/client/hooks/use-search.ts +1 -1
  81. package/src/client/hooks/use-sidebar.ts +48 -2
  82. package/src/client/hooks/use-tabs.ts +6 -2
  83. package/src/client/hooks/use-version.ts +3 -3
  84. package/src/client/index.ts +22 -22
  85. package/src/client/ssg/boltdocs-shell.tsx +127 -0
  86. package/src/client/ssg/create-routes.tsx +179 -0
  87. package/src/client/ssg/index.ts +3 -0
  88. package/src/client/ssg/mdx-page.tsx +37 -0
  89. package/src/client/store/boltdocs-context.tsx +46 -99
  90. package/src/client/theme/neutral.css +90 -50
  91. package/src/client/types.ts +5 -33
  92. package/src/client/utils/react-to-text.ts +34 -0
  93. package/dist/cache-Cr8W2zgZ.cjs +0 -6
  94. package/dist/cache-DFdakSmR.mjs +0 -6
  95. package/dist/client/index.mjs +0 -6
  96. package/dist/client/ssr.cjs +0 -6
  97. package/dist/client/ssr.d.cts +0 -80
  98. package/dist/client/ssr.d.mts +0 -80
  99. package/dist/client/ssr.mjs +0 -6
  100. package/dist/node-CWXme96p.mjs +0 -73
  101. package/dist/node-VYfhzGrh.cjs +0 -73
  102. package/dist/search-dialog-BeNyI_KQ.mjs +0 -6
  103. package/dist/search-dialog-dYsCAk5S.js +0 -6
  104. package/dist/use-search-D25n0PrV.mjs +0 -6
  105. package/dist/use-search-WuzdH1cJ.js +0 -6
  106. package/src/client/app/index.tsx +0 -348
  107. package/src/client/app/mdx-page.tsx +0 -15
  108. package/src/client/app/preload.tsx +0 -66
  109. package/src/client/app/router.tsx +0 -30
  110. package/src/client/integrations/codesandbox.ts +0 -179
  111. package/src/client/integrations/index.ts +0 -1
  112. package/src/client/ssr.tsx +0 -65
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "boltdocs",
3
- "version": "2.5.6",
3
+ "version": "2.6.0",
4
4
  "description": "A lightweight documentation generator for React projects.",
5
- "main": "dist/node/index.js",
5
+ "main": "dist/node/index.mjs",
6
6
  "module": "dist/node/index.mjs",
7
7
  "types": "dist/node/index.d.ts",
8
8
  "bin": {
@@ -11,7 +11,7 @@
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
14
- "type": "commonjs",
14
+ "type": "module",
15
15
  "sideEffects": [
16
16
  "**/*.css"
17
17
  ],
@@ -19,17 +19,12 @@
19
19
  ".": {
20
20
  "types": "./dist/node/index.d.ts",
21
21
  "import": "./dist/node/index.mjs",
22
- "require": "./dist/node/index.js"
22
+ "require": "./dist/node/index.cjs"
23
23
  },
24
24
  "./client": {
25
25
  "types": "./dist/client/index.d.ts",
26
- "import": "./dist/client/index.mjs",
27
- "require": "./dist/client/index.js"
28
- },
29
- "./ssr": {
30
- "types": "./dist/client/ssr.d.ts",
31
- "import": "./dist/client/ssr.mjs",
32
- "require": "./dist/client/ssr.js"
26
+ "import": "./dist/client/index.js",
27
+ "require": "./dist/client/index.cjs"
33
28
  },
34
29
  "./theme/neutral.css": "./dist/client/theme/neutral.css"
35
30
  },
@@ -54,7 +49,7 @@
54
49
  "dependencies": {
55
50
  "@mdx-js/rollup": "3.1.1",
56
51
  "@tailwindcss/vite": "4.2.2",
57
- "@vitejs/plugin-react": "^5.0.0",
52
+ "@vitejs/plugin-react": "6.0.1",
58
53
  "cac": "7.0.0",
59
54
  "class-variance-authority": "0.7.1",
60
55
  "clsx": "2.1.1",
@@ -65,7 +60,8 @@
65
60
  "isomorphic-dompurify": "3.7.1",
66
61
  "lucide-react": "0.575.0",
67
62
  "react-aria-components": "1.16.0",
68
- "react-router-dom": "6.30.3",
63
+ "react-helmet-async": "^2.0.1",
64
+ "react-router-dom": "^7.0.0",
69
65
  "rehype-slug": "6.0.0",
70
66
  "remark-frontmatter": "5.0.0",
71
67
  "remark-gfm": "4.0.1",
@@ -75,22 +71,23 @@
75
71
  "svgo": "4.0.1",
76
72
  "tailwind-merge": "3.5.0",
77
73
  "unist-util-visit": "5.1.0",
78
- "vite": "7.3.1",
74
+ "vite": "7.0.0 || 8.0.0",
79
75
  "vite-plugin-image-optimizer": "2.0.3",
80
- "zod": "4.3.6"
76
+ "zod": "4.3.6",
77
+ "@bdocs/ssg": "0.0.1"
81
78
  },
82
79
  "peerDependencies": {
83
- "react": "19.2.5",
84
- "react-dom": "19.2.5"
80
+ "react": "19.0.0",
81
+ "react-dom": "19.0.0"
85
82
  },
86
83
  "devDependencies": {
87
- "@types/node": "25.3.2",
88
- "@types/react": "^19.2.14",
89
- "@types/react-dom": "^19.2.3",
84
+ "@types/node": "22.0.0",
85
+ "@types/react": "19.0.0",
86
+ "@types/react-dom": "19.0.0",
90
87
  "@types/react-router-dom": "5.3.3",
91
88
  "@types/semver": "7.7.1",
92
- "tsdown": "^0.21.7",
93
- "typescript": "^5.9.3"
89
+ "tsdown": "0.21.7",
90
+ "typescript": "5.9.3"
94
91
  },
95
92
  "scripts": {
96
93
  "build": "tsdown --config-loader unrun",
@@ -1,18 +1,51 @@
1
1
  import { createContext, use } from 'react'
2
- import type { BoltdocsConfig } from '@node/config'
2
+ import type { BoltdocsConfig } from '../../shared/types'
3
3
 
4
4
  /**
5
5
  * Context for the global documentation configuration.
6
+ * Using a global singleton pattern to survive dual-package or duplicated-code hazards.
6
7
  */
7
- export const ConfigContext = createContext<BoltdocsConfig | null>(null)
8
+ const CONFIG_CONTEXT_SYMBOL = Symbol.for('__BDOCS_CONFIG_CONTEXT__')
9
+ const CONFIG_INSTANCE_SYMBOL = Symbol.for('__BDOCS_CONFIG_INSTANCE__')
10
+
11
+ export const ConfigContext =
12
+ (globalThis as any)[CONFIG_CONTEXT_SYMBOL] ||
13
+ ((globalThis as any)[CONFIG_CONTEXT_SYMBOL] =
14
+ createContext<BoltdocsConfig | null>(null))
15
+
16
+ export function ConfigProvider({
17
+ config,
18
+ children,
19
+ }: {
20
+ config: BoltdocsConfig
21
+ children: React.ReactNode
22
+ }) {
23
+ // Sync with global registry for dual-package fallback
24
+ if (typeof globalThis !== 'undefined') {
25
+ ;(globalThis as any)[CONFIG_INSTANCE_SYMBOL] = config
26
+ }
27
+
28
+ return (
29
+ <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>
30
+ )
31
+ }
8
32
 
9
33
  /**
10
34
  * Hook to access the Boltdocs configuration.
11
35
  */
12
36
  export function useConfig() {
13
37
  const context = use(ConfigContext)
14
- if (!context) {
15
- throw new Error('useConfig must be used within a ConfigProvider')
38
+
39
+ // Fallback to global registry if context is missing (dual-package hazard safety net)
40
+ if (
41
+ !context &&
42
+ typeof globalThis !== 'undefined' &&
43
+ (globalThis as any)[CONFIG_INSTANCE_SYMBOL]
44
+ ) {
45
+ return (globalThis as any)[CONFIG_INSTANCE_SYMBOL] as BoltdocsConfig
16
46
  }
17
- return context
47
+
48
+ if (!context)
49
+ throw new Error('useConfig must be used within a ConfigProvider')
50
+ return context as BoltdocsConfig
18
51
  }
@@ -0,0 +1,34 @@
1
+ import UserLayout from 'virtual:boltdocs-layout'
2
+ import { useMdxComponents } from './mdx-components-context'
3
+ import { useMemo } from 'react'
4
+
5
+ /**
6
+ * DocPage is a layout wrapper for documentation content during SSG.
7
+ * It renders the user-defined layout (or default) around the MDX content.
8
+ */
9
+ export function DocPage({
10
+ route,
11
+ content: Content,
12
+ mdxComponents: propComponents,
13
+ }: any) {
14
+ // Access global MDX components (defaults + plugins + virtuals) from context
15
+ const contextComponents = useMdxComponents()
16
+
17
+ // Merge components: Prop components (from loader) take priority,
18
+ // then context components (globals).
19
+ const allComponents = useMemo(
20
+ () => ({
21
+ ...contextComponents,
22
+ ...propComponents,
23
+ }),
24
+ [contextComponents, propComponents],
25
+ )
26
+
27
+ if (!Content) return null
28
+
29
+ return (
30
+ <UserLayout route={route}>
31
+ <Content components={allComponents} />
32
+ </UserLayout>
33
+ )
34
+ }
@@ -1,6 +1,5 @@
1
- import type React from 'react'
2
1
  import { Link as LucideLink } from 'lucide-react'
3
- import * as MdxComponents from '@components/mdx'
2
+ import * as MdxComponents from '../components/mdx'
4
3
 
5
4
  const Heading = ({
6
5
  level,
@@ -25,7 +24,7 @@ const Heading = ({
25
24
  )
26
25
  }
27
26
 
28
- import { Loading } from '@components/ui-base/loading'
27
+ import { Loading } from '../components/ui-base/loading'
29
28
 
30
29
  export const mdxComponentsDefault = {
31
30
  ...MdxComponents,
@@ -2,10 +2,30 @@ import { createContext, use } from 'react'
2
2
 
3
3
  export type MdxComponentsType = Record<string, React.ComponentType<any>>
4
4
 
5
- const MdxComponentsContext = createContext<MdxComponentsType>({})
5
+ const MDX_COMPONENTS_CONTEXT_SYMBOL = Symbol.for(
6
+ '__BDOCS_MDX_COMPONENTS_CONTEXT__',
7
+ )
8
+ const MDX_COMPONENTS_INSTANCE_SYMBOL = Symbol.for(
9
+ '__BDOCS_MDX_COMPONENTS_INSTANCE__',
10
+ )
11
+
12
+ const MdxComponentsContext =
13
+ (globalThis as any)[MDX_COMPONENTS_CONTEXT_SYMBOL] ||
14
+ ((globalThis as any)[MDX_COMPONENTS_CONTEXT_SYMBOL] =
15
+ createContext<MdxComponentsType>({}))
6
16
 
7
17
  export function useMdxComponents() {
8
- return use(MdxComponentsContext)
18
+ const context = use(MdxComponentsContext)
19
+
20
+ // Fallback to global registry for dual-package hazards
21
+ if (
22
+ (!context || Object.keys(context).length === 0) &&
23
+ (globalThis as any)[MDX_COMPONENTS_INSTANCE_SYMBOL]
24
+ ) {
25
+ return (globalThis as any)[MDX_COMPONENTS_INSTANCE_SYMBOL]
26
+ }
27
+
28
+ return context
9
29
  }
10
30
 
11
31
  export function MdxComponentsProvider({
@@ -15,6 +35,11 @@ export function MdxComponentsProvider({
15
35
  components: MdxComponentsType
16
36
  children: React.ReactNode
17
37
  }) {
38
+ // Sync with global registry
39
+ if (typeof globalThis !== 'undefined') {
40
+ ;(globalThis as any)[MDX_COMPONENTS_INSTANCE_SYMBOL] = components
41
+ }
42
+
18
43
  return (
19
44
  <MdxComponentsContext.Provider value={components}>
20
45
  {children}
@@ -0,0 +1,34 @@
1
+ import { createContext, useContext } from 'react'
2
+ import type { ComponentRoute } from '../types'
3
+
4
+ interface RoutesContextType {
5
+ routes: ComponentRoute[]
6
+ }
7
+
8
+ const RoutesContext = createContext<RoutesContextType>({
9
+ routes: [],
10
+ })
11
+
12
+ /**
13
+ * Hook to access the processed routes list from the closest provider.
14
+ */
15
+ export function useRoutesContext() {
16
+ return useContext(RoutesContext)
17
+ }
18
+
19
+ /**
20
+ * Provider component for the documentation routes.
21
+ */
22
+ export function RoutesProvider({
23
+ routes,
24
+ children,
25
+ }: {
26
+ routes: ComponentRoute[]
27
+ children: React.ReactNode
28
+ }) {
29
+ return (
30
+ <RoutesContext.Provider value={{ routes }}>
31
+ {children}
32
+ </RoutesContext.Provider>
33
+ )
34
+ }
@@ -12,19 +12,19 @@ export function ScrollHandler() {
12
12
  // biome-ignore lint/correctness/useExhaustiveDependencies: pathname is used as a trigger for scroll-to-top on navigation
13
13
  useLayoutEffect(() => {
14
14
  const container = document.querySelector('.boltdocs-content') || window
15
-
15
+
16
16
  // Helper to get scroll top
17
17
  const getScrollTop = () => {
18
18
  if (container === window) return window.scrollY
19
19
  return (container as HTMLElement).scrollTop
20
20
  }
21
-
21
+
22
22
  // Helper to scroll
23
23
  const scrollTo = (top: number, behavior: ScrollBehavior = 'auto') => {
24
24
  if (container === window) {
25
25
  window.scrollTo({ top, behavior })
26
26
  } else {
27
- (container as HTMLElement).scrollTo({ top, behavior })
27
+ ;(container as HTMLElement).scrollTo({ top, behavior })
28
28
  }
29
29
  }
30
30
 
@@ -33,7 +33,10 @@ export function ScrollHandler() {
33
33
  const element = document.getElementById(id)
34
34
  if (element) {
35
35
  const offset = 80
36
- const containerTop = container === window ? 0 : (container as HTMLElement).getBoundingClientRect().top
36
+ const containerTop =
37
+ container === window
38
+ ? 0
39
+ : (container as HTMLElement).getBoundingClientRect().top
37
40
  const elementRect = element.getBoundingClientRect().top
38
41
  const elementPosition = elementRect - containerTop
39
42
  const offsetPosition = elementPosition - offset + getScrollTop()
@@ -1,99 +1,103 @@
1
- import { createContext, use, useEffect, useState } from 'react'
1
+ import { createContext, use, useState, useEffect } from 'react'
2
2
 
3
- type Theme = 'light' | 'dark' | 'system'
3
+ export type Theme = 'light' | 'dark' | 'system'
4
+ export type ResolvedTheme = 'light' | 'dark'
4
5
 
5
6
  interface ThemeContextType {
6
7
  theme: Theme
7
- resolvedTheme: 'light' | 'dark'
8
+ resolvedTheme: ResolvedTheme
8
9
  setTheme: (theme: Theme) => void
9
10
  }
10
11
 
11
- const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
12
+ const THEME_CONTEXT_SYMBOL = Symbol.for('__BDOCS_THEME_CONTEXT__')
13
+ const THEME_INSTANCE_SYMBOL = Symbol.for('__BDOCS_THEME_INSTANCE__')
14
+ const THEME_EVENT = 'boltdocs-theme-change'
15
+
16
+ const ThemeContext =
17
+ (globalThis as any)[THEME_CONTEXT_SYMBOL] ||
18
+ ((globalThis as any)[THEME_CONTEXT_SYMBOL] = createContext<
19
+ ThemeContextType | undefined
20
+ >(undefined))
12
21
 
13
22
  export function ThemeProvider({ children }: { children: React.ReactNode }) {
14
23
  const [theme, setThemeState] = useState<Theme>('system')
15
- const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('dark')
16
- const [mounted, setMounted] = useState(false)
17
-
18
- // Initialize theme from localStorage and set internal resolved theme
19
- useEffect(() => {
20
- setMounted(true)
21
- const stored = localStorage.getItem('boltdocs-theme') as Theme | null
22
- const initialTheme =
23
- stored === 'light' || stored === 'dark' || stored === 'system'
24
- ? stored
25
- : 'system'
26
-
27
- setThemeState(initialTheme)
24
+ const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>('dark')
28
25
 
26
+ const applyTheme = (targetTheme: Theme) => {
29
27
  const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
28
+ const isDark =
29
+ targetTheme === 'dark' ||
30
+ (targetTheme === 'system' && mediaQuery.matches)
31
+
32
+ const root = window.document.documentElement
33
+ root.classList.toggle('dark', isDark)
34
+ root.dataset.theme = isDark ? 'dark' : 'light'
35
+ setResolvedTheme(isDark ? 'dark' : 'light')
36
+ }
30
37
 
31
- const updateResolved = (currentTheme: Theme, isDark: boolean) => {
32
- if (currentTheme === 'system') {
33
- setResolvedTheme(isDark ? 'dark' : 'light')
34
- } else {
35
- setResolvedTheme(currentTheme as 'light' | 'dark')
36
- }
38
+ useEffect(() => {
39
+ const savedTheme = localStorage.getItem('boltdocs-theme') as Theme | null
40
+ if (savedTheme) {
41
+ setThemeState(savedTheme)
42
+ applyTheme(savedTheme)
43
+ } else {
44
+ applyTheme('system')
37
45
  }
38
46
 
39
- updateResolved(initialTheme, mediaQuery.matches)
40
-
41
- const handleChange = (e: MediaQueryListEvent) => {
42
- // Re-read current theme state from some stable ref would be better, but we can capture it
43
- // actually, the second useEffect will handle the source of truth,
44
- // but this listener ensures 'system' updates instantly.
45
- setResolvedTheme((prevResolved) => {
46
- const currentTheme =
47
- (localStorage.getItem('boltdocs-theme') as Theme) || 'system'
48
- if (currentTheme === 'system') {
49
- return e.matches ? 'dark' : 'light'
50
- }
51
- return prevResolved
52
- })
47
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
48
+ const listener = () => {
49
+ const current = (localStorage.getItem('boltdocs-theme') as Theme) || 'system'
50
+ if (current === 'system') applyTheme('system')
53
51
  }
54
52
 
55
- mediaQuery.addEventListener('change', handleChange)
56
- return () => mediaQuery.removeEventListener('change', handleChange)
53
+ mediaQuery.addEventListener('change', listener)
54
+ return () => mediaQuery.removeEventListener('change', listener)
57
55
  }, [])
58
56
 
59
- // Sync with DOM and resolved theme when theme preference changes
60
- useEffect(() => {
61
- if (!mounted) return
62
-
63
- const isSystemDark = window.matchMedia(
64
- '(prefers-color-scheme: dark)',
65
- ).matches
66
- const nextResolved =
67
- theme === 'system' ? (isSystemDark ? 'dark' : 'light') : theme
68
-
69
- setResolvedTheme(nextResolved as 'light' | 'dark')
70
-
71
- const root = document.documentElement
72
- if (nextResolved === 'light') {
73
- root.classList.add('theme-light')
74
- root.dataset.theme = 'light'
75
- } else {
76
- root.classList.remove('theme-light')
77
- root.dataset.theme = 'dark'
78
- }
79
- }, [theme, mounted])
80
-
81
57
  const setTheme = (newTheme: Theme) => {
82
58
  setThemeState(newTheme)
83
59
  localStorage.setItem('boltdocs-theme', newTheme)
60
+ applyTheme(newTheme)
61
+
62
+ // Notify external listeners (dual-package hazard)
63
+ if (typeof window !== 'undefined') {
64
+ window.dispatchEvent(new CustomEvent(THEME_EVENT, { detail: newTheme }))
65
+ }
84
66
  }
85
67
 
86
- return (
87
- <ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}>
88
- {children}
89
- </ThemeContext.Provider>
90
- )
68
+ const value = { theme, resolvedTheme, setTheme }
69
+
70
+ // Sync with global registry
71
+ if (typeof globalThis !== 'undefined') {
72
+ ;(globalThis as any)[THEME_INSTANCE_SYMBOL] = value
73
+ }
74
+
75
+ return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
91
76
  }
92
77
 
93
78
  export function useTheme() {
94
79
  const context = use(ThemeContext)
80
+ const [, forceUpdate] = useState({})
81
+
82
+ useEffect(() => {
83
+ if (context) return
84
+
85
+ const handler = () => forceUpdate({})
86
+ window.addEventListener(THEME_EVENT, handler)
87
+ return () => window.removeEventListener(THEME_EVENT, handler)
88
+ }, [context])
89
+
90
+ // Fallback to global registry for dual-package hazards
91
+ if (
92
+ !context &&
93
+ typeof globalThis !== 'undefined' &&
94
+ (globalThis as any)[THEME_INSTANCE_SYMBOL]
95
+ ) {
96
+ return (globalThis as any)[THEME_INSTANCE_SYMBOL] as ThemeContextType
97
+ }
98
+
95
99
  if (context === undefined) {
96
100
  throw new Error('useTheme must be used within a ThemeProvider')
97
101
  }
98
- return context
102
+ return context as ThemeContextType
99
103
  }
@@ -1,18 +1,17 @@
1
- import { DocsLayout } from '@components/docs-layout'
2
- import { Navbar } from '@components/ui-base/navbar'
3
- import { Sidebar } from '@components/ui-base/sidebar'
4
- import { OnThisPage } from '@components/ui-base/on-this-page'
5
- import { Head } from '@components/ui-base/head'
6
- import { Breadcrumbs } from '@components/ui-base/breadcrumbs'
7
- import { PageNav } from '@components/ui-base/page-nav'
8
- import { ErrorBoundary } from '@components/ui-base/error-boundary'
9
- import { CopyMarkdown } from '@components/ui-base/copy-markdown'
10
- import { useRoutes } from '@client/hooks/use-routes'
11
- import { useConfig } from '@client/app/config-context'
12
- import { useMdxComponents } from '@client/app/mdx-components-context'
13
-
1
+ import { DocsLayout } from './docs-layout'
2
+ import { Navbar } from './ui-base/navbar'
3
+ import { Sidebar } from './ui-base/sidebar'
4
+ import { OnThisPage } from './ui-base/on-this-page'
5
+ import { Head } from './ui-base/head'
6
+ import { Breadcrumbs } from './ui-base/breadcrumbs'
7
+ import { PageNav } from './ui-base/page-nav'
8
+ import { ErrorBoundary } from './ui-base/error-boundary'
9
+ import { CopyMarkdown } from './ui-base/copy-markdown'
10
+ import { useRoutes } from '../hooks/use-routes'
11
+ import { useConfig } from '../app/config-context'
12
+ import { useMdxComponents } from '../app/mdx-components-context'
14
13
  import { useLocation } from 'react-router-dom'
15
- import { getTranslated } from '@client/utils/i18n'
14
+ import { getTranslated } from '../utils/i18n'
16
15
 
17
16
  export interface LayoutProps {
18
17
  children: React.ReactNode
@@ -1,5 +1,4 @@
1
- import type React from 'react'
2
- import { cn } from '@client/utils/cn'
1
+ import { cn } from '../utils/cn'
3
2
  import { useLocation } from '../hooks'
4
3
 
5
4
  /**
@@ -1,6 +1,4 @@
1
- import type { SVGProps } from 'react'
2
-
3
- type WrapperProps = SVGProps<SVGSVGElement> & {
1
+ type WrapperProps = React.SVGProps<SVGSVGElement> & {
4
2
  size?: number
5
3
  }
6
4
 
@@ -150,7 +148,6 @@ export const BracketsOrange = (props: WrapperProps) => (
150
148
  </svg>
151
149
  )
152
150
 
153
- export default BracketsOrange
154
151
 
155
152
  export const React = (props: WrapperProps) => (
156
153
  <svg
@@ -219,10 +216,44 @@ export const Yaml = (props: WrapperProps) => (
219
216
  viewBox="0 0 24 24"
220
217
  {...wrapperProps(props)}
221
218
  >
222
- <title>{'YAML'}</title>
219
+ <title>YAML</title>
223
220
  <path
224
221
  fill="#A78BFA"
225
222
  d="M6.533 5.864h2.755l2.654 5.011h.113l2.654-5.011h2.756l-4.245 7.522V17.5h-2.443v-4.114L6.533 5.864z"
226
223
  ></path>
227
224
  </svg>
228
225
  )
226
+
227
+ export const Rust = (props: WrapperProps) => (
228
+ <svg
229
+ xmlns="http://www.w3.org/2000/svg"
230
+ fill="none"
231
+ viewBox="0 0 25 24"
232
+ {...wrapperProps(props)}
233
+ >
234
+ <title>Rust</title>
235
+ <path
236
+ fill="#EA580C"
237
+ fillRule="evenodd"
238
+ d="M12.58 2.136a.287.287 0 00-.488 0l-.526.85a8.974 8.974 0 00-.232.022l-.683-.73a.287.287 0 00-.478.096l-.35.935c-.075.021-.15.044-.224.067l-.812-.583a.287.287 0 00-.45.187l-.162.989a9.088 9.088 0 00-.204.11l-.913-.417a.287.287 0 00-.406.272l.036 1.005a9.099 9.099 0 00-.175.144l-.98-.231a.287.287 0 00-.345.345l.231.98a9.125 9.125 0 00-.144.175L4.27 6.316a.287.287 0 00-.271.406l.416.913a9.036 9.036 0 00-.11.203L3.317 8a.287.287 0 00-.187.45l.584.813a8.953 8.953 0 00-.068.223l-.935.35a.287.287 0 00-.095.479l.73.682a8.966 8.966 0 00-.023.233l-.85.526a.287.287 0 000 .488l.85.526c.007.078.014.156.023.233l-.73.682a.287.287 0 00.095.479l.935.35.068.223-.584.812a.287.287 0 00.187.451l.99.162c.035.068.072.135.109.203l-.416.913a.287.287 0 00.271.406l1.006-.036c.047.059.095.117.143.174l-.23.981a.287.287 0 00.344.345l.981-.23c.057.048.115.095.174.142l-.036 1.006a.287.287 0 00.406.272l.914-.417c.067.038.135.074.203.11l.161.99a.287.287 0 00.451.186l.813-.584c.074.024.148.046.223.068l.35.935a.287.287 0 00.478.095l.683-.73c.077.01.154.017.232.023l.526.85a.287.287 0 00.489 0l.526-.85c.078-.006.155-.014.232-.023l.682.73a.287.287 0 00.479-.095l.35-.935c.075-.022.15-.044.223-.068l.813.584a.287.287 0 00.45-.187l.162-.99a8.77 8.77 0 00.203-.109l.913.417a.287.287 0 00.406-.272l-.035-1.006c.058-.047.116-.094.174-.143l.98.231a.287.287 0 00.346-.345l-.231-.98.143-.175 1.006.036a.287.287 0 00.271-.406l-.416-.913a9.4 9.4 0 00.109-.203l.99-.162a.287.287 0 00.187-.45l-.584-.813a8.43 8.43 0 00.067-.223l.935-.35a.287.287 0 00.096-.479l-.73-.682c.009-.077.016-.155.023-.233l.85-.526a.287.287 0 000-.488l-.85-.526a8.844 8.844 0 00-.023-.233l.73-.682a.287.287 0 00-.096-.479l-.934-.35a9.246 9.246 0 00-.068-.223l.584-.812A.287.287 0 0021.357 8l-.99-.162a8.92 8.92 0 00-.11-.203l.417-.913a.287.287 0 00-.271-.406l-1.006.036a9.178 9.178 0 00-.143-.174l.23-.981a.287.287 0 00-.345-.345l-.98.23a9.43 9.43 0 00-.174-.142l.035-1.006a.287.287 0 00-.405-.272l-.914.417a9.11 9.11 0 00-.203-.11l-.162-.99a.287.287 0 00-.45-.186l-.813.584a9.088 9.088 0 00-.223-.068l-.35-.935a.287.287 0 00-.479-.095l-.682.73a9.062 9.062 0 00-.232-.023l-.526-.85zm-.257 1.62a.592.592 0 01.578.596.595.595 0 11-.578-.595zm1.363.98A7.324 7.324 0 0118.7 8.309l-.702 1.585a.547.547 0 00.275.717l1.352.6c.041.422.047.847.015 1.27h-.752c-.075 0-.106.05-.106.123v.344c0 .812-.457.99-.859 1.034-.383.044-.806-.161-.86-.394-.22-1.24-.583-1.526-1.152-1.975l-.041-.033c.736-.467 1.502-1.158 1.502-2.08 0-.998-.683-1.625-1.148-1.934-.655-.43-1.379-.516-1.574-.516H6.88a7.324 7.324 0 014.098-2.312l.916.96a.54.54 0 00.766.018l1.026-.978zm-8.46 4.407a.595.595 0 11-.034 1.19.595.595 0 01.034-1.19zm14.192.026a.595.595 0 11-.035 1.19.595.595 0 01.035-1.19zm-13.07.096h1.037v4.678H5.291a7.324 7.324 0 01-.237-2.797l1.282-.57a.542.542 0 00.276-.716l-.264-.595zm4.33.05h2.47c.128 0 .901.147.901.727 0 .48-.593.653-1.081.653h-2.293l.002-1.38zm0 3.36h1.892c.172 0 .924.05 1.164 1.011.026.104.064.291.107.503.078.389.174.861.247 1.06.113.345.57 1.034 1.058 1.034h3.089c-.207.277-.433.54-.677.785l-1.258-.27a.544.544 0 00-.645.417l-.298 1.394a7.323 7.323 0 01-6.108-.03l-.298-1.392a.542.542 0 00-.643-.418l-1.23.264a7.32 7.32 0 01-.636-.75h5.984c.067 0 .113-.011.113-.074v-2.117c0-.061-.046-.075-.113-.075h-1.75l.001-1.341zm-2.763 4.848a.595.595 0 11-.034 1.19.595.595 0 01.034-1.19zm8.814.027a.596.596 0 11-.035 1.19.596.596 0 01.035-1.19z"
239
+ clipRule="evenodd"
240
+ ></path>
241
+ </svg>
242
+ );
243
+
244
+
245
+ export const BracketsRed = (props: WrapperProps) => (
246
+ <svg
247
+ xmlns="http://www.w3.org/2000/svg"
248
+ fill="none"
249
+ viewBox="0 0 24 24"
250
+ {...wrapperProps(props)}
251
+ >
252
+ <title>Rust</title>
253
+ <path
254
+ fill="#F87171"
255
+ d="M4.778 6.667A2.667 2.667 0 017.444 4a.889.889 0 010 1.778.889.889 0 00-.888.889v3.5c0 .701-.273 1.35-.73 1.833.457.483.73 1.132.73 1.832v3.501c0 .491.398.89.888.89a.889.889 0 010 1.777 2.667 2.667 0 01-2.666-2.667v-3.5a.889.889 0 00-.674-.863l-.43-.108a.889.889 0 010-1.724l.43-.108a.889.889 0 00.674-.862V6.667zm14.222 0A2.667 2.667 0 0016.333 4a.889.889 0 000 1.778c.491 0 .89.398.89.889v3.5c0 .701.272 1.35.729 1.833a2.664 2.664 0 00-.73 1.832v3.501a.889.889 0 01-.889.89.889.889 0 000 1.777A2.667 2.667 0 0019 17.333v-3.5c0-.408.278-.764.673-.863l.431-.108a.889.889 0 000-1.724l-.43-.108a.889.889 0 01-.674-.862V6.667z"
256
+ ></path>
257
+ </svg>
258
+ );
259
+
@@ -7,7 +7,7 @@ import {
7
7
  Zap,
8
8
  Flame,
9
9
  } from 'lucide-react'
10
- import { cn } from '@client/utils/cn'
10
+ import { cn } from '../../utils/cn'
11
11
  import { cva } from 'class-variance-authority'
12
12
  import type { VariantProps } from 'class-variance-authority'
13
13
 
@@ -21,26 +21,17 @@ const ICON_MAP: Record<string, React.ReactNode> = {
21
21
  caution: <Zap size={18} />,
22
22
  }
23
23
 
24
- const LABEL_MAP: Record<string, string> = {
25
- note: 'Note',
26
- tip: 'Tip',
27
- info: 'Info',
28
- warning: 'Warning',
29
- danger: 'Danger',
30
- important: 'Important',
31
- caution: 'Caution',
32
- }
33
24
 
34
- const admonitionVariants = cva('py-4 px-4 rounded-lg', {
25
+ const admonitionVariants = cva('py-4 px-4 rounded-lg flex items-center gap-3 border-[1px] flex-row', {
35
26
  variants: {
36
27
  type: {
37
- note: 'border-primary-400 bg-primary-500/5 text-primary-400',
38
- tip: 'border-emerald-500 bg-emerald-500/5 text-emerald-500',
39
- info: 'border-sky-500 bg-sky-500/5 text-sky-500',
40
- warning: 'border-amber-500 bg-amber-500/5 text-amber-500',
41
- danger: 'border-red-500 bg-red-500/5 text-red-500',
42
- important: 'border-orange-500 bg-orange-500/5 text-orange-500',
43
- caution: 'border-yellow-500 bg-yellow-500/5 text-yellow-500',
28
+ note: 'border-primary-200 dark:border-primary-800 bg-primary-500/5 text-primary-400',
29
+ tip: 'border-emerald-200 dark:border-emerald-800 bg-emerald-500/5 text-emerald-500',
30
+ info: 'border-sky-200 dark:border-sky-800 bg-sky-500/5 text-sky-500',
31
+ warning: 'border-amber-200 dark:border-amber-800 bg-amber-500/5 text-amber-500',
32
+ danger: 'border-red-200 dark:border-red-800/70 bg-red-500/5 text-red-500',
33
+ important: 'border-orange-200 dark:border-orange-800/70 bg-orange-500/5 text-orange-500',
34
+ caution: 'border-yellow-200 dark:border-yellow-800/70 bg-yellow-500/5 text-yellow-500',
44
35
  },
45
36
  },
46
37
  defaultVariants: {
@@ -51,7 +42,7 @@ type AdmonitionVariants = VariantProps<typeof admonitionVariants>
51
42
 
52
43
  export interface AdmonitionProps
53
44
  extends React.HTMLAttributes<HTMLDivElement>,
54
- AdmonitionVariants {
45
+ AdmonitionVariants {
55
46
  title?: string
56
47
  children: React.ReactNode
57
48
  }
@@ -69,14 +60,7 @@ export function Admonition({
69
60
  role={type === 'warning' || type === 'danger' ? 'alert' : 'note'}
70
61
  {...rest}
71
62
  >
72
- <div className="flex items-center flex-row gap-2 mb-2">
73
- <span className={cn('shrink-0', admonitionVariants({ type }))}>
74
- {ICON_MAP[type as keyof typeof ICON_MAP]}
75
- </span>
76
- <span className="text-sm font-bold tracking-tight text-text-main">
77
- {title || LABEL_MAP[type as keyof typeof LABEL_MAP]}
78
- </span>
79
- </div>
63
+ {ICON_MAP[type as keyof typeof ICON_MAP]}
80
64
  <div className="text-sm text-text-muted leading-relaxed [&>p]:m-0 [&>p]:mb-2 [&>p:last-child]:mb-0">
81
65
  {children}
82
66
  </div>