@windrun-huaiin/third-ui 31.0.1 → 31.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/fuma/base/custom-header.d.ts +3 -1
  2. package/dist/fuma/base/custom-header.js +13 -7
  3. package/dist/fuma/base/custom-header.mjs +13 -7
  4. package/dist/fuma/base/custom-home-layout.d.ts +6 -1
  5. package/dist/fuma/base/custom-home-layout.js +6 -3
  6. package/dist/fuma/base/custom-home-layout.mjs +6 -3
  7. package/dist/fuma/base/docs-root-provider.d.ts +1 -6
  8. package/dist/fuma/base/docs-root-provider.js +2 -8
  9. package/dist/fuma/base/docs-root-provider.mjs +2 -8
  10. package/dist/fuma/base/header-theme-switch.d.ts +2 -1
  11. package/dist/fuma/base/site-docs-layout.d.ts +1 -0
  12. package/dist/fuma/base/site-docs-layout.js +22 -2
  13. package/dist/fuma/base/site-docs-layout.mjs +22 -2
  14. package/dist/fuma/base/site-home-layout.js +2 -2
  15. package/dist/fuma/base/site-home-layout.mjs +2 -2
  16. package/dist/fuma/base/site-layout-shared.d.ts +5 -1
  17. package/dist/fuma/base/site-layout-shared.js +1 -1
  18. package/dist/fuma/base/site-layout-shared.mjs +1 -1
  19. package/dist/fuma/base/site-theme-provider.d.ts +8 -0
  20. package/dist/fuma/base/site-theme-provider.js +41 -0
  21. package/dist/fuma/base/site-theme-provider.mjs +39 -0
  22. package/package.json +1 -1
  23. package/src/fuma/base/custom-header.tsx +30 -6
  24. package/src/fuma/base/custom-home-layout.tsx +24 -12
  25. package/src/fuma/base/docs-root-provider.tsx +3 -30
  26. package/src/fuma/base/header-theme-switch.tsx +2 -1
  27. package/src/fuma/base/site-docs-layout.tsx +23 -2
  28. package/src/fuma/base/site-home-layout.tsx +1 -0
  29. package/src/fuma/base/site-layout-shared.tsx +11 -2
  30. package/src/fuma/base/site-theme-provider.tsx +59 -0
@@ -1,11 +1,12 @@
1
1
  import { type CSSProperties } from 'react';
2
2
  import { HomeLayoutProps } from 'fumadocs-ui/layouts/home';
3
+ import type { SiteThemeSwitchConfig } from './site-layout-shared';
3
4
  export type NavbarCSSVars = CSSProperties & {
4
5
  '--fd-banner-height'?: string;
5
6
  '--fd-header-height'?: string;
6
7
  '--fd-nav-max-width'?: string;
7
8
  };
8
- export interface CustomHomeHeaderProps extends HomeLayoutProps {
9
+ export interface CustomHomeHeaderProps extends Omit<HomeLayoutProps, 'themeSwitch'> {
9
10
  /**
10
11
  * Banner height in rem units
11
12
  *
@@ -46,6 +47,7 @@ export interface CustomHomeHeaderProps extends HomeLayoutProps {
46
47
  * Control order of utilities inside the mobile dropdown.
47
48
  */
48
49
  mobileMenuActionsOrder?: MobileMenuAction[];
50
+ themeSwitch?: SiteThemeSwitchConfig;
49
51
  }
50
52
  export type DesktopAction = 'search' | 'theme' | 'i18n' | 'secondary' | 'github';
51
53
  export type MobileBarAction = 'pinned' | 'search' | 'menu';
@@ -35,7 +35,7 @@ const DEFAULT_MOBILE_MENU_ACTIONS = [
35
35
  'theme',
36
36
  ];
37
37
  function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitch = {}, searchToggle = {}, bannerHeight = 0, headerHeight = 2.5, maxContentWidth = 1400, navbarClassName, floating = false, desktopActionsOrder = DEFAULT_DESKTOP_ACTIONS, mobileBarActionsOrder = DEFAULT_MOBILE_BAR_ACTIONS, mobileMenuActionsOrder = DEFAULT_MOBILE_MENU_ACTIONS, }) {
38
- var _a, _b, _c, _d, _e, _f, _g;
38
+ var _a, _b, _c, _d, _e;
39
39
  const finalLinks = React.useMemo(() => shared.resolveLinkItems({ links, githubUrl }), [links, githubUrl]);
40
40
  const navItems = finalLinks.filter((item) => { var _a; return ['nav', 'all'].includes((_a = item.on) !== null && _a !== void 0 ? _a : 'all'); });
41
41
  const menuItems = finalLinks.filter((item) => { var _a; return ['menu', 'all'].includes((_a = item.on) !== null && _a !== void 0 ? _a : 'all'); });
@@ -55,8 +55,8 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
55
55
  search: searchToggle.enabled !== false
56
56
  ? (_b = (_a = searchToggle.components) === null || _a === void 0 ? void 0 : _a.lg) !== null && _b !== void 0 ? _b : null
57
57
  : null,
58
- theme: themeSwitch.enabled !== false
59
- ? (_c = themeSwitch.component) !== null && _c !== void 0 ? _c : jsxRuntime.jsx(headerThemeSwitch.HeaderThemeSwitch, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })
58
+ theme: shouldShowThemeSwitch(themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode)
59
+ ? (jsxRuntime.jsx(headerThemeSwitch.HeaderThemeSwitch, { mode: normalizeThemeSwitchMode(themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode) }))
60
60
  : null,
61
61
  i18n: i18n ? (jsxRuntime.jsx(CompactLanguageToggle, { children: jsxRuntime.jsx(icons.LanguagesIcon, { className: "size-5" }) })) : null,
62
62
  secondary: desktopSecondaryDisplayItems.length ? (jsxRuntime.jsx("ul", { className: "flex flex-row gap-2 items-center empty:hidden", children: desktopSecondaryDisplayItems.map((item, i) => (jsxRuntime.jsx(NavbarLinkItem, { item: item, className: utils.cn(item.type === 'icon' && [
@@ -78,8 +78,8 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
78
78
  github: githubMobileMenuItem ? (jsxRuntime.jsx(MenuLinkItem, { item: githubMobileMenuItem, className: "-me-1.5" })) : null,
79
79
  separator: jsxRuntime.jsx("div", { role: "separator", className: "flex-1" }),
80
80
  i18n: i18n ? (jsxRuntime.jsxs(CompactLanguageToggle, { children: [jsxRuntime.jsx(icons.LanguagesIcon, { className: "size-5" }), jsxRuntime.jsx(languageSelect.LanguageSelectText, {}), jsxRuntime.jsx(icons.ChevronDownIcon, { className: "size-3 text-fd-muted-foreground" })] })) : null,
81
- theme: themeSwitch.enabled !== false
82
- ? (_d = themeSwitch.component) !== null && _d !== void 0 ? _d : jsxRuntime.jsx(headerThemeSwitch.HeaderThemeSwitch, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })
81
+ theme: shouldShowThemeSwitch(themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode)
82
+ ? (jsxRuntime.jsx(headerThemeSwitch.HeaderThemeSwitch, { mode: normalizeThemeSwitchMode(themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode) }))
83
83
  : null,
84
84
  };
85
85
  const shouldRenderMobileUtilities = mobileMenuActionsOrder.some((action) => action !== 'separator' && Boolean(mobileMenuActionNodes[action]));
@@ -100,7 +100,7 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
100
100
  }) })) : null] })] }));
101
101
  const mobilePinnedNode = mobilePinnedItems.length > 0 ? (jsxRuntime.jsx(jsxRuntime.Fragment, { children: mobilePinnedItems.map((item, i) => (jsxRuntime.jsx(NavbarLinkItem, { item: item, className: "max-sm:-mr-1" }, `mobile-pinned-${i}`))) })) : null;
102
102
  const mobileSearchNode = searchToggle.enabled !== false
103
- ? (_f = (_e = searchToggle.components) === null || _e === void 0 ? void 0 : _e.sm) !== null && _f !== void 0 ? _f : null
103
+ ? (_d = (_c = searchToggle.components) === null || _c === void 0 ? void 0 : _c.sm) !== null && _d !== void 0 ? _d : null
104
104
  : null;
105
105
  const mobileBarNodes = {
106
106
  pinned: mobilePinnedNode,
@@ -108,7 +108,7 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
108
108
  menu: menuNode,
109
109
  };
110
110
  const getMobileBarNode = (action) => { var _a; return (_a = mobileBarNodes[action]) !== null && _a !== void 0 ? _a : null; };
111
- return (jsxRuntime.jsxs(CustomNavbar, { bannerHeight: bannerHeight, headerHeight: headerHeight, maxContentWidth: maxContentWidth, className: navbarClassName, floating: floating, children: [jsxRuntime.jsx(Link, { href: (_g = nav.url) !== null && _g !== void 0 ? _g : '/', prefetch: false, className: "inline-flex items-center gap-2.5 font-semibold", children: renderNavTitle(nav.title) }), nav.children, jsxRuntime.jsx("ul", { className: "flex flex-row items-center gap-2 px-6 max-sm:hidden", children: navItems
111
+ return (jsxRuntime.jsxs(CustomNavbar, { bannerHeight: bannerHeight, headerHeight: headerHeight, maxContentWidth: maxContentWidth, className: navbarClassName, floating: floating, children: [jsxRuntime.jsx(Link, { href: (_e = nav.url) !== null && _e !== void 0 ? _e : '/', prefetch: false, className: "inline-flex items-center gap-2.5 font-semibold", children: renderNavTitle(nav.title) }), nav.children, jsxRuntime.jsx("ul", { className: "flex flex-row items-center gap-2 px-6 max-sm:hidden", children: navItems
112
112
  .filter((item) => !isSecondary(item))
113
113
  .map((item, i) => (jsxRuntime.jsx(NavbarLinkItem, { item: item, className: "text-sm" }, i))) }), jsxRuntime.jsx("div", { className: "flex flex-row items-center justify-end gap-1.5 flex-1 max-lg:hidden", children: desktopActionsOrder.map((action) => {
114
114
  const node = desktopActionNodes[action];
@@ -120,6 +120,12 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
120
120
  return node ? (jsxRuntime.jsx(React.Fragment, { children: node }, `mobile-bar-${action}`)) : null;
121
121
  }) })] }));
122
122
  }
123
+ function shouldShowThemeSwitch(mode) {
124
+ return mode === 'light-dark' || mode === 'light-dark-system' || mode == null;
125
+ }
126
+ function normalizeThemeSwitchMode(mode) {
127
+ return mode === 'light-dark' ? 'light-dark' : 'light-dark-system';
128
+ }
123
129
  function CustomNavbar(_a) {
124
130
  var { bannerHeight = 0, headerHeight = 2.5, maxContentWidth = 1400, className, style, floating = false } = _a, props = tslib.__rest(_a, ["bannerHeight", "headerHeight", "maxContentWidth", "className", "style", "floating"]);
125
131
  const [value, setValue] = React.useState('');
@@ -33,7 +33,7 @@ const DEFAULT_MOBILE_MENU_ACTIONS = [
33
33
  'theme',
34
34
  ];
35
35
  function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitch = {}, searchToggle = {}, bannerHeight = 0, headerHeight = 2.5, maxContentWidth = 1400, navbarClassName, floating = false, desktopActionsOrder = DEFAULT_DESKTOP_ACTIONS, mobileBarActionsOrder = DEFAULT_MOBILE_BAR_ACTIONS, mobileMenuActionsOrder = DEFAULT_MOBILE_MENU_ACTIONS, }) {
36
- var _a, _b, _c, _d, _e, _f, _g;
36
+ var _a, _b, _c, _d, _e;
37
37
  const finalLinks = useMemo(() => resolveLinkItems({ links, githubUrl }), [links, githubUrl]);
38
38
  const navItems = finalLinks.filter((item) => { var _a; return ['nav', 'all'].includes((_a = item.on) !== null && _a !== void 0 ? _a : 'all'); });
39
39
  const menuItems = finalLinks.filter((item) => { var _a; return ['menu', 'all'].includes((_a = item.on) !== null && _a !== void 0 ? _a : 'all'); });
@@ -53,8 +53,8 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
53
53
  search: searchToggle.enabled !== false
54
54
  ? (_b = (_a = searchToggle.components) === null || _a === void 0 ? void 0 : _a.lg) !== null && _b !== void 0 ? _b : null
55
55
  : null,
56
- theme: themeSwitch.enabled !== false
57
- ? (_c = themeSwitch.component) !== null && _c !== void 0 ? _c : jsx(HeaderThemeSwitch, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })
56
+ theme: shouldShowThemeSwitch(themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode)
57
+ ? (jsx(HeaderThemeSwitch, { mode: normalizeThemeSwitchMode(themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode) }))
58
58
  : null,
59
59
  i18n: i18n ? (jsx(CompactLanguageToggle, { children: jsx(LanguagesIcon, { className: "size-5" }) })) : null,
60
60
  secondary: desktopSecondaryDisplayItems.length ? (jsx("ul", { className: "flex flex-row gap-2 items-center empty:hidden", children: desktopSecondaryDisplayItems.map((item, i) => (jsx(NavbarLinkItem, { item: item, className: cn(item.type === 'icon' && [
@@ -76,8 +76,8 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
76
76
  github: githubMobileMenuItem ? (jsx(MenuLinkItem, { item: githubMobileMenuItem, className: "-me-1.5" })) : null,
77
77
  separator: jsx("div", { role: "separator", className: "flex-1" }),
78
78
  i18n: i18n ? (jsxs(CompactLanguageToggle, { children: [jsx(LanguagesIcon, { className: "size-5" }), jsx(LanguageSelectText, {}), jsx(ChevronDownIcon, { className: "size-3 text-fd-muted-foreground" })] })) : null,
79
- theme: themeSwitch.enabled !== false
80
- ? (_d = themeSwitch.component) !== null && _d !== void 0 ? _d : jsx(HeaderThemeSwitch, { mode: themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode })
79
+ theme: shouldShowThemeSwitch(themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode)
80
+ ? (jsx(HeaderThemeSwitch, { mode: normalizeThemeSwitchMode(themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode) }))
81
81
  : null,
82
82
  };
83
83
  const shouldRenderMobileUtilities = mobileMenuActionsOrder.some((action) => action !== 'separator' && Boolean(mobileMenuActionNodes[action]));
@@ -98,7 +98,7 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
98
98
  }) })) : null] })] }));
99
99
  const mobilePinnedNode = mobilePinnedItems.length > 0 ? (jsx(Fragment, { children: mobilePinnedItems.map((item, i) => (jsx(NavbarLinkItem, { item: item, className: "max-sm:-mr-1" }, `mobile-pinned-${i}`))) })) : null;
100
100
  const mobileSearchNode = searchToggle.enabled !== false
101
- ? (_f = (_e = searchToggle.components) === null || _e === void 0 ? void 0 : _e.sm) !== null && _f !== void 0 ? _f : null
101
+ ? (_d = (_c = searchToggle.components) === null || _c === void 0 ? void 0 : _c.sm) !== null && _d !== void 0 ? _d : null
102
102
  : null;
103
103
  const mobileBarNodes = {
104
104
  pinned: mobilePinnedNode,
@@ -106,7 +106,7 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
106
106
  menu: menuNode,
107
107
  };
108
108
  const getMobileBarNode = (action) => { var _a; return (_a = mobileBarNodes[action]) !== null && _a !== void 0 ? _a : null; };
109
- return (jsxs(CustomNavbar, { bannerHeight: bannerHeight, headerHeight: headerHeight, maxContentWidth: maxContentWidth, className: navbarClassName, floating: floating, children: [jsx(Link, { href: (_g = nav.url) !== null && _g !== void 0 ? _g : '/', prefetch: false, className: "inline-flex items-center gap-2.5 font-semibold", children: renderNavTitle(nav.title) }), nav.children, jsx("ul", { className: "flex flex-row items-center gap-2 px-6 max-sm:hidden", children: navItems
109
+ return (jsxs(CustomNavbar, { bannerHeight: bannerHeight, headerHeight: headerHeight, maxContentWidth: maxContentWidth, className: navbarClassName, floating: floating, children: [jsx(Link, { href: (_e = nav.url) !== null && _e !== void 0 ? _e : '/', prefetch: false, className: "inline-flex items-center gap-2.5 font-semibold", children: renderNavTitle(nav.title) }), nav.children, jsx("ul", { className: "flex flex-row items-center gap-2 px-6 max-sm:hidden", children: navItems
110
110
  .filter((item) => !isSecondary(item))
111
111
  .map((item, i) => (jsx(NavbarLinkItem, { item: item, className: "text-sm" }, i))) }), jsx("div", { className: "flex flex-row items-center justify-end gap-1.5 flex-1 max-lg:hidden", children: desktopActionsOrder.map((action) => {
112
112
  const node = desktopActionNodes[action];
@@ -118,6 +118,12 @@ function CustomHomeHeader({ nav = {}, i18n = false, links, githubUrl, themeSwitc
118
118
  return node ? (jsx(Fragment$1, { children: node }, `mobile-bar-${action}`)) : null;
119
119
  }) })] }));
120
120
  }
121
+ function shouldShowThemeSwitch(mode) {
122
+ return mode === 'light-dark' || mode === 'light-dark-system' || mode == null;
123
+ }
124
+ function normalizeThemeSwitchMode(mode) {
125
+ return mode === 'light-dark' ? 'light-dark' : 'light-dark-system';
126
+ }
121
127
  function CustomNavbar(_a) {
122
128
  var { bannerHeight = 0, headerHeight = 2.5, maxContentWidth = 1400, className, style, floating = false } = _a, props = __rest(_a, ["bannerHeight", "headerHeight", "maxContentWidth", "className", "style", "floating"]);
123
129
  const [value, setValue] = useState('');
@@ -1,6 +1,7 @@
1
1
  import type { CSSProperties, ReactNode } from 'react';
2
2
  import { type HomeLayoutProps } from 'fumadocs-ui/layouts/home';
3
3
  import { type DesktopAction, type MobileBarAction, type MobileMenuAction } from './custom-header';
4
+ import type { SiteThemeSwitchConfig } from './site-layout-shared';
4
5
  export interface CustomHomeLayoutProps {
5
6
  locale: string;
6
7
  options: HomeLayoutProps;
@@ -76,6 +77,10 @@ export interface CustomHomeLayoutProps {
76
77
  * The default locale for the application (default: 'en')
77
78
  */
78
79
  defaultLocale?: string;
80
+ /**
81
+ * Theme mode for this layout group.
82
+ */
83
+ themeSwitch?: SiteThemeSwitchConfig;
79
84
  children?: ReactNode;
80
85
  }
81
86
  export interface HeaderActionOrders {
@@ -83,7 +88,7 @@ export interface HeaderActionOrders {
83
88
  mobileBar?: MobileBarAction[];
84
89
  mobileMenu?: MobileMenuAction[];
85
90
  }
86
- export declare function CustomHomeLayout({ locale, options, children, showBanner, bannerHeight, headerHeight, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter, showGoToTop, style, floatingNav, actionOrders, localePrefixAsNeeded, defaultLocale, }: CustomHomeLayoutProps): import("react/jsx-runtime").JSX.Element;
91
+ export declare function CustomHomeLayout({ locale, options, children, showBanner, bannerHeight, headerHeight, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter, showGoToTop, style, floatingNav, actionOrders, localePrefixAsNeeded, defaultLocale, themeSwitch, }: CustomHomeLayoutProps): import("react/jsx-runtime").JSX.Element;
87
92
  export declare function HomeTitle({ children, className, }: {
88
93
  children: ReactNode;
89
94
  className?: string;
@@ -6,9 +6,11 @@ var home = require('fumadocs-ui/layouts/home');
6
6
  var fumaBannerSuit = require('../fuma-banner-suit.js');
7
7
  var footer = require('../../main/footer.js');
8
8
  var goToTop = require('../../main/go-to-top.js');
9
+ var siteThemeProvider = require('./site-theme-provider.js');
9
10
  var customHeader = require('./custom-header.js');
10
11
 
11
- function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer: footer$1, goToTop: goToTop$1, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, localePrefixAsNeeded = true, defaultLocale = 'en', }) {
12
+ function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer: footer$1, goToTop: goToTop$1, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, localePrefixAsNeeded = true, defaultLocale = 'en', themeSwitch, }) {
13
+ var _a;
12
14
  const resolvedBannerHeight = bannerHeight !== null && bannerHeight !== void 0 ? bannerHeight : (showBanner ? 3 : 0.5);
13
15
  const resolvedPaddingTop = headerPaddingTop !== null && headerPaddingTop !== void 0 ? headerPaddingTop : (showBanner ? 0 : 0.5);
14
16
  const layoutStyle = Object.assign({ '--fd-banner-height': `${resolvedBannerHeight}rem`, '--fd-header-height': `${headerHeight}rem`, paddingTop: floatingNav
@@ -16,8 +18,9 @@ function CustomHomeLayout({ locale, options, children, showBanner = false, banne
16
18
  : `${resolvedPaddingTop}rem` }, style);
17
19
  const { nav } = options, homeLayoutProps = tslib.__rest(options, ["nav"]);
18
20
  const navOptions = nav !== null && nav !== void 0 ? nav : {};
19
- const header = (jsxRuntime.jsx(customHeader.CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav, desktopActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.desktop, mobileBarActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileBar, mobileMenuActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileMenu })));
20
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsxRuntime.jsx(fumaBannerSuit.FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxRuntime.jsxs(home.HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer$1 !== null && footer$1 !== void 0 ? footer$1 : jsxRuntime.jsx(footer.Footer, { locale: locale, localePrefixAsNeeded: localePrefixAsNeeded, defaultLocale: defaultLocale }) : null, showGoToTop ? goToTop$1 !== null && goToTop$1 !== void 0 ? goToTop$1 : jsxRuntime.jsx(goToTop.GoToTop, {}) : null] }))] }));
21
+ const header = (jsxRuntime.jsx(customHeader.CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, themeSwitch: themeSwitch, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav, desktopActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.desktop, mobileBarActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileBar, mobileMenuActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileMenu })));
22
+ const themeMode = (_a = themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode) !== null && _a !== void 0 ? _a : 'light-dark-system';
23
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsxRuntime.jsx(fumaBannerSuit.FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxRuntime.jsx(siteThemeProvider.SiteThemeProvider, { mode: themeMode, children: jsxRuntime.jsxs(home.HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer$1 !== null && footer$1 !== void 0 ? footer$1 : jsxRuntime.jsx(footer.Footer, { locale: locale, localePrefixAsNeeded: localePrefixAsNeeded, defaultLocale: defaultLocale }) : null, showGoToTop ? goToTop$1 !== null && goToTop$1 !== void 0 ? goToTop$1 : jsxRuntime.jsx(goToTop.GoToTop, {}) : null] })) })] }));
21
24
  }
22
25
  function HomeTitle({ children, className, }) {
23
26
  return (jsxRuntime.jsx("span", { className: `font-medium in-[.uwu]:hidden in-[header]:text-[clamp(12px,3vw,15px)]! ${className !== null && className !== void 0 ? className : ''}`, children: children }));
@@ -4,9 +4,11 @@ import { HomeLayout } from 'fumadocs-ui/layouts/home';
4
4
  import { FumaBannerSuit } from '../fuma-banner-suit.mjs';
5
5
  import { Footer } from '../../main/footer.mjs';
6
6
  import { GoToTop } from '../../main/go-to-top.mjs';
7
+ import { SiteThemeProvider } from './site-theme-provider.mjs';
7
8
  import { CustomHomeHeader } from './custom-header.mjs';
8
9
 
9
- function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, localePrefixAsNeeded = true, defaultLocale = 'en', }) {
10
+ function CustomHomeLayout({ locale, options, children, showBanner = false, bannerHeight, headerHeight = 2.5, headerPaddingTop, navbarClassName, banner, footer, goToTop, showFooter = true, showGoToTop = true, style, floatingNav = false, actionOrders, localePrefixAsNeeded = true, defaultLocale = 'en', themeSwitch, }) {
11
+ var _a;
10
12
  const resolvedBannerHeight = bannerHeight !== null && bannerHeight !== void 0 ? bannerHeight : (showBanner ? 3 : 0.5);
11
13
  const resolvedPaddingTop = headerPaddingTop !== null && headerPaddingTop !== void 0 ? headerPaddingTop : (showBanner ? 0 : 0.5);
12
14
  const layoutStyle = Object.assign({ '--fd-banner-height': `${resolvedBannerHeight}rem`, '--fd-header-height': `${headerHeight}rem`, paddingTop: floatingNav
@@ -14,8 +16,9 @@ function CustomHomeLayout({ locale, options, children, showBanner = false, banne
14
16
  : `${resolvedPaddingTop}rem` }, style);
15
17
  const { nav } = options, homeLayoutProps = __rest(options, ["nav"]);
16
18
  const navOptions = nav !== null && nav !== void 0 ? nav : {};
17
- const header = (jsx(CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav, desktopActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.desktop, mobileBarActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileBar, mobileMenuActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileMenu })));
18
- return (jsxs(Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsx(FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsxs(HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer !== null && footer !== void 0 ? footer : jsx(Footer, { locale: locale, localePrefixAsNeeded: localePrefixAsNeeded, defaultLocale: defaultLocale }) : null, showGoToTop ? goToTop !== null && goToTop !== void 0 ? goToTop : jsx(GoToTop, {}) : null] }))] }));
19
+ const header = (jsx(CustomHomeHeader, Object.assign({}, homeLayoutProps, { nav: navOptions, themeSwitch: themeSwitch, bannerHeight: resolvedBannerHeight, headerHeight: headerHeight, navbarClassName: navbarClassName, floating: floatingNav, desktopActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.desktop, mobileBarActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileBar, mobileMenuActionsOrder: actionOrders === null || actionOrders === void 0 ? void 0 : actionOrders.mobileMenu })));
20
+ const themeMode = (_a = themeSwitch === null || themeSwitch === void 0 ? void 0 : themeSwitch.mode) !== null && _a !== void 0 ? _a : 'light-dark-system';
21
+ return (jsxs(Fragment, { children: [banner !== null && banner !== void 0 ? banner : (jsx(FumaBannerSuit, { locale: locale, showBanner: showBanner, floating: floatingNav })), jsx(SiteThemeProvider, { mode: themeMode, children: jsxs(HomeLayout, Object.assign({}, homeLayoutProps, { nav: Object.assign(Object.assign({}, navOptions), { component: header }), className: 'bg-neutral-100 dark:bg-neutral-900', style: layoutStyle, children: [children, showFooter ? footer !== null && footer !== void 0 ? footer : jsx(Footer, { locale: locale, localePrefixAsNeeded: localePrefixAsNeeded, defaultLocale: defaultLocale }) : null, showGoToTop ? goToTop !== null && goToTop !== void 0 ? goToTop : jsx(GoToTop, {}) : null] })) })] }));
19
22
  }
20
23
  function HomeTitle({ children, className, }) {
21
24
  return (jsx("span", { className: `font-medium in-[.uwu]:hidden in-[header]:text-[clamp(12px,3vw,15px)]! ${className !== null && className !== void 0 ? className : ''}`, children: children }));
@@ -1,19 +1,14 @@
1
1
  import type { ComponentProps, ReactNode } from 'react';
2
2
  import { NextProvider } from 'fumadocs-core/framework/next';
3
3
  import { type I18nProviderProps } from 'fumadocs-ui/contexts/i18n';
4
- import { type ThemeProviderProps } from 'next-themes';
5
4
  type NextProviderComponents = {
6
5
  Link?: ComponentProps<typeof NextProvider>['Link'];
7
6
  Image?: ComponentProps<typeof NextProvider>['Image'];
8
7
  };
9
- type ThemeOptions = ThemeProviderProps & {
10
- enabled?: boolean;
11
- };
12
8
  export interface DocsRootProviderProps {
13
9
  i18n: Omit<I18nProviderProps, 'children'>;
14
- theme?: ThemeOptions;
15
10
  components?: NextProviderComponents;
16
11
  children: ReactNode;
17
12
  }
18
- export declare function DocsRootProvider({ i18n, theme, components, children, }: DocsRootProviderProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function DocsRootProvider({ i18n, components, children, }: DocsRootProviderProps): import("react/jsx-runtime").JSX.Element;
19
14
  export {};
@@ -3,15 +3,9 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var next = require('fumadocs-core/framework/next');
5
5
  var i18n = require('fumadocs-ui/contexts/i18n');
6
- var nextThemes = require('next-themes');
7
6
 
8
- function DocsRootProvider({ i18n: i18n$1, theme = {}, components, children, }) {
9
- let body = children;
10
- if (theme.enabled !== false) {
11
- body = (jsxRuntime.jsx(nextThemes.ThemeProvider, Object.assign({ attribute: "class", defaultTheme: "system", enableSystem: true, disableTransitionOnChange: true }, theme, { children: body })));
12
- }
13
- body = (jsxRuntime.jsx(i18n.I18nProvider, Object.assign({}, i18n$1, { children: body })));
14
- return (jsxRuntime.jsx(next.NextProvider, { Link: components === null || components === void 0 ? void 0 : components.Link, Image: components === null || components === void 0 ? void 0 : components.Image, children: body }));
7
+ function DocsRootProvider({ i18n: i18n$1, components, children, }) {
8
+ return (jsxRuntime.jsx(next.NextProvider, { Link: components === null || components === void 0 ? void 0 : components.Link, Image: components === null || components === void 0 ? void 0 : components.Image, children: jsxRuntime.jsx(i18n.I18nProvider, Object.assign({}, i18n$1, { children: children })) }));
15
9
  }
16
10
 
17
11
  exports.DocsRootProvider = DocsRootProvider;
@@ -1,15 +1,9 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { NextProvider } from 'fumadocs-core/framework/next';
3
3
  import { I18nProvider } from 'fumadocs-ui/contexts/i18n';
4
- import { ThemeProvider } from 'next-themes';
5
4
 
6
- function DocsRootProvider({ i18n, theme = {}, components, children, }) {
7
- let body = children;
8
- if (theme.enabled !== false) {
9
- body = (jsx(ThemeProvider, Object.assign({ attribute: "class", defaultTheme: "system", enableSystem: true, disableTransitionOnChange: true }, theme, { children: body })));
10
- }
11
- body = (jsx(I18nProvider, Object.assign({}, i18n, { children: body })));
12
- return (jsx(NextProvider, { Link: components === null || components === void 0 ? void 0 : components.Link, Image: components === null || components === void 0 ? void 0 : components.Image, children: body }));
5
+ function DocsRootProvider({ i18n, components, children, }) {
6
+ return (jsx(NextProvider, { Link: components === null || components === void 0 ? void 0 : components.Link, Image: components === null || components === void 0 ? void 0 : components.Image, children: jsx(I18nProvider, Object.assign({}, i18n, { children: children })) }));
13
7
  }
14
8
 
15
9
  export { DocsRootProvider };
@@ -1,5 +1,6 @@
1
1
  import { type ComponentProps } from 'react';
2
+ import type { SiteThemeSwitchMode } from './site-layout-shared';
2
3
  export interface HeaderThemeSwitchProps extends ComponentProps<'div'> {
3
- mode?: 'light-dark' | 'light-dark-system';
4
+ mode?: Exclude<SiteThemeSwitchMode, 'light-only' | 'dark-only'>;
4
5
  }
5
6
  export declare function HeaderThemeSwitch({ className, mode, ...props }: HeaderThemeSwitchProps): import("react/jsx-runtime").JSX.Element;
@@ -4,6 +4,7 @@ import { type SiteBaseLayoutConfig } from './site-layout-shared';
4
4
  export interface SiteDocsLayoutConfig extends SiteBaseLayoutConfig {
5
5
  tree: DocsLayoutProps['tree'];
6
6
  sidebar?: DocsLayoutProps['sidebar'];
7
+ themeProvider?: boolean;
7
8
  }
8
9
  export declare function SiteDocsLayout({ config, children, }: {
9
10
  config: SiteDocsLayoutConfig;
@@ -3,13 +3,33 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var docs = require('fumadocs-ui/layouts/docs');
5
5
  var siteLayoutShared = require('./site-layout-shared.js');
6
+ var siteThemeProvider = require('./site-theme-provider.js');
6
7
 
7
8
  function toDocsLayoutOptions(config) {
8
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (config.nav ? { nav: config.nav } : {})), (config.i18n ? { i18n: config.i18n } : {})), (config.githubUrl ? { githubUrl: config.githubUrl } : {})), (config.links ? { links: siteLayoutShared.normalizeNavItems(config.links) } : {})), (config.searchToggle ? { searchToggle: config.searchToggle } : {})), (config.themeSwitch ? { themeSwitch: config.themeSwitch } : {})), (config.sidebar ? { sidebar: config.sidebar } : {})), { tree: config.tree });
9
+ var _a, _b;
10
+ const themeMode = (_b = (_a = config.themeSwitch) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'light-dark-system';
11
+ const shouldShowThemeSwitch = config.themeProvider !== false &&
12
+ (themeMode === 'light-dark' || themeMode === 'light-dark-system');
13
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (config.nav ? { nav: config.nav } : {})), (config.i18n ? { i18n: config.i18n } : {})), (config.githubUrl ? { githubUrl: config.githubUrl } : {})), (config.links ? { links: siteLayoutShared.normalizeNavItems(config.links) } : {})), (config.searchToggle ? { searchToggle: config.searchToggle } : {})), (shouldShowThemeSwitch
14
+ ? {
15
+ themeSwitch: {
16
+ mode: themeMode,
17
+ },
18
+ }
19
+ : {
20
+ themeSwitch: {
21
+ enabled: false,
22
+ },
23
+ })), (config.sidebar ? { sidebar: config.sidebar } : {})), { tree: config.tree });
9
24
  }
10
25
  function SiteDocsLayout({ config, children, }) {
26
+ var _a, _b;
11
27
  const options = toDocsLayoutOptions(config);
12
- return jsxRuntime.jsx(docs.DocsLayout, Object.assign({}, options, { children: children }));
28
+ const themeMode = (_b = (_a = config.themeSwitch) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'light-dark-system';
29
+ const body = jsxRuntime.jsx(docs.DocsLayout, Object.assign({}, options, { children: children }));
30
+ if (config.themeProvider === false)
31
+ return body;
32
+ return jsxRuntime.jsx(siteThemeProvider.SiteThemeProvider, { mode: themeMode, children: body });
13
33
  }
14
34
 
15
35
  exports.SiteDocsLayout = SiteDocsLayout;
@@ -1,13 +1,33 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { DocsLayout } from 'fumadocs-ui/layouts/docs';
3
3
  import { normalizeNavItems } from './site-layout-shared.mjs';
4
+ import { SiteThemeProvider } from './site-theme-provider.mjs';
4
5
 
5
6
  function toDocsLayoutOptions(config) {
6
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (config.nav ? { nav: config.nav } : {})), (config.i18n ? { i18n: config.i18n } : {})), (config.githubUrl ? { githubUrl: config.githubUrl } : {})), (config.links ? { links: normalizeNavItems(config.links) } : {})), (config.searchToggle ? { searchToggle: config.searchToggle } : {})), (config.themeSwitch ? { themeSwitch: config.themeSwitch } : {})), (config.sidebar ? { sidebar: config.sidebar } : {})), { tree: config.tree });
7
+ var _a, _b;
8
+ const themeMode = (_b = (_a = config.themeSwitch) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'light-dark-system';
9
+ const shouldShowThemeSwitch = config.themeProvider !== false &&
10
+ (themeMode === 'light-dark' || themeMode === 'light-dark-system');
11
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (config.nav ? { nav: config.nav } : {})), (config.i18n ? { i18n: config.i18n } : {})), (config.githubUrl ? { githubUrl: config.githubUrl } : {})), (config.links ? { links: normalizeNavItems(config.links) } : {})), (config.searchToggle ? { searchToggle: config.searchToggle } : {})), (shouldShowThemeSwitch
12
+ ? {
13
+ themeSwitch: {
14
+ mode: themeMode,
15
+ },
16
+ }
17
+ : {
18
+ themeSwitch: {
19
+ enabled: false,
20
+ },
21
+ })), (config.sidebar ? { sidebar: config.sidebar } : {})), { tree: config.tree });
7
22
  }
8
23
  function SiteDocsLayout({ config, children, }) {
24
+ var _a, _b;
9
25
  const options = toDocsLayoutOptions(config);
10
- return jsx(DocsLayout, Object.assign({}, options, { children: children }));
26
+ const themeMode = (_b = (_a = config.themeSwitch) === null || _a === void 0 ? void 0 : _a.mode) !== null && _b !== void 0 ? _b : 'light-dark-system';
27
+ const body = jsx(DocsLayout, Object.assign({}, options, { children: children }));
28
+ if (config.themeProvider === false)
29
+ return body;
30
+ return jsx(SiteThemeProvider, { mode: themeMode, children: body });
11
31
  }
12
32
 
13
33
  export { SiteDocsLayout };
@@ -8,8 +8,8 @@ var siteLayoutShared = require('./site-layout-shared.js');
8
8
  function SiteHomeLayout({ locale, config, children, }) {
9
9
  const { actionOrders, banner, bannerHeight, defaultLocale, floatingNav, footer, goToTop, headerHeight, headerPaddingTop, localePrefixAsNeeded, navbarClassName, showBanner, showFooter, showGoToTop } = config, baseConfig = tslib.__rest(config, ["actionOrders", "banner", "bannerHeight", "defaultLocale", "floatingNav", "footer", "goToTop", "headerHeight", "headerPaddingTop", "localePrefixAsNeeded", "navbarClassName", "showBanner", "showFooter", "showGoToTop"]);
10
10
  const options = siteLayoutShared.toHomeLayoutOptions(baseConfig);
11
- const layoutProps = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ locale,
12
- options }, (actionOrders ? { actionOrders } : {})), (banner ? { banner } : {})), (bannerHeight != null ? { bannerHeight } : {})), (defaultLocale ? { defaultLocale } : {})), (floatingNav != null ? { floatingNav } : {})), (footer ? { footer } : {})), (goToTop ? { goToTop } : {})), (headerHeight != null ? { headerHeight } : {})), (headerPaddingTop != null ? { headerPaddingTop } : {})), (localePrefixAsNeeded != null ? { localePrefixAsNeeded } : {})), (navbarClassName ? { navbarClassName } : {})), (showBanner != null ? { showBanner } : {})), (showFooter != null ? { showFooter } : {})), (showGoToTop != null ? { showGoToTop } : {}));
11
+ const layoutProps = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ locale,
12
+ options }, (actionOrders ? { actionOrders } : {})), (banner ? { banner } : {})), (bannerHeight != null ? { bannerHeight } : {})), (defaultLocale ? { defaultLocale } : {})), (floatingNav != null ? { floatingNav } : {})), (footer ? { footer } : {})), (goToTop ? { goToTop } : {})), (headerHeight != null ? { headerHeight } : {})), (headerPaddingTop != null ? { headerPaddingTop } : {})), (localePrefixAsNeeded != null ? { localePrefixAsNeeded } : {})), (navbarClassName ? { navbarClassName } : {})), (showBanner != null ? { showBanner } : {})), (showFooter != null ? { showFooter } : {})), (showGoToTop != null ? { showGoToTop } : {})), (config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}));
13
13
  return jsxRuntime.jsx(customHomeLayout.CustomHomeLayout, Object.assign({}, layoutProps, { children: children }));
14
14
  }
15
15
 
@@ -6,8 +6,8 @@ import { toHomeLayoutOptions } from './site-layout-shared.mjs';
6
6
  function SiteHomeLayout({ locale, config, children, }) {
7
7
  const { actionOrders, banner, bannerHeight, defaultLocale, floatingNav, footer, goToTop, headerHeight, headerPaddingTop, localePrefixAsNeeded, navbarClassName, showBanner, showFooter, showGoToTop } = config, baseConfig = __rest(config, ["actionOrders", "banner", "bannerHeight", "defaultLocale", "floatingNav", "footer", "goToTop", "headerHeight", "headerPaddingTop", "localePrefixAsNeeded", "navbarClassName", "showBanner", "showFooter", "showGoToTop"]);
8
8
  const options = toHomeLayoutOptions(baseConfig);
9
- const layoutProps = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ locale,
10
- options }, (actionOrders ? { actionOrders } : {})), (banner ? { banner } : {})), (bannerHeight != null ? { bannerHeight } : {})), (defaultLocale ? { defaultLocale } : {})), (floatingNav != null ? { floatingNav } : {})), (footer ? { footer } : {})), (goToTop ? { goToTop } : {})), (headerHeight != null ? { headerHeight } : {})), (headerPaddingTop != null ? { headerPaddingTop } : {})), (localePrefixAsNeeded != null ? { localePrefixAsNeeded } : {})), (navbarClassName ? { navbarClassName } : {})), (showBanner != null ? { showBanner } : {})), (showFooter != null ? { showFooter } : {})), (showGoToTop != null ? { showGoToTop } : {}));
9
+ const layoutProps = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ locale,
10
+ options }, (actionOrders ? { actionOrders } : {})), (banner ? { banner } : {})), (bannerHeight != null ? { bannerHeight } : {})), (defaultLocale ? { defaultLocale } : {})), (floatingNav != null ? { floatingNav } : {})), (footer ? { footer } : {})), (goToTop ? { goToTop } : {})), (headerHeight != null ? { headerHeight } : {})), (headerPaddingTop != null ? { headerPaddingTop } : {})), (localePrefixAsNeeded != null ? { localePrefixAsNeeded } : {})), (navbarClassName ? { navbarClassName } : {})), (showBanner != null ? { showBanner } : {})), (showFooter != null ? { showFooter } : {})), (showGoToTop != null ? { showGoToTop } : {})), (config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}));
11
11
  return jsx(CustomHomeLayout, Object.assign({}, layoutProps, { children: children }));
12
12
  }
13
13
 
@@ -47,7 +47,11 @@ export interface SiteBaseLayoutConfig {
47
47
  githubUrl?: string;
48
48
  links?: SiteNavItemConfig[];
49
49
  searchToggle?: HomeLayoutProps['searchToggle'];
50
- themeSwitch?: HomeLayoutProps['themeSwitch'];
50
+ themeSwitch?: SiteThemeSwitchConfig;
51
+ }
52
+ export type SiteThemeSwitchMode = 'light-dark-system' | 'light-dark' | 'light-only' | 'dark-only';
53
+ export interface SiteThemeSwitchConfig {
54
+ mode?: SiteThemeSwitchMode;
51
55
  }
52
56
  export interface SiteMenuLeafConfig {
53
57
  text: ReactNode;
@@ -38,7 +38,7 @@ function createSiteBaseLayoutConfig(options) {
38
38
  } }, (options.i18n ? { i18n: options.i18n } : {})), (options.githubUrl ? { githubUrl: options.githubUrl } : {}));
39
39
  }
40
40
  function toHomeLayoutOptions(config) {
41
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (config.nav ? { nav: config.nav } : {})), (config.i18n ? { i18n: config.i18n } : {})), (config.githubUrl ? { githubUrl: config.githubUrl } : {})), (config.links ? { links: normalizeNavItems(config.links) } : {})), (config.searchToggle ? { searchToggle: config.searchToggle } : {})), (config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}));
41
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (config.nav ? { nav: config.nav } : {})), (config.i18n ? { i18n: config.i18n } : {})), (config.githubUrl ? { githubUrl: config.githubUrl } : {})), (config.links ? { links: normalizeNavItems(config.links) } : {})), (config.searchToggle ? { searchToggle: config.searchToggle } : {}));
42
42
  }
43
43
 
44
44
  exports.createSiteBaseLayoutConfig = createSiteBaseLayoutConfig;
@@ -36,7 +36,7 @@ function createSiteBaseLayoutConfig(options) {
36
36
  } }, (options.i18n ? { i18n: options.i18n } : {})), (options.githubUrl ? { githubUrl: options.githubUrl } : {}));
37
37
  }
38
38
  function toHomeLayoutOptions(config) {
39
- return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (config.nav ? { nav: config.nav } : {})), (config.i18n ? { i18n: config.i18n } : {})), (config.githubUrl ? { githubUrl: config.githubUrl } : {})), (config.links ? { links: normalizeNavItems(config.links) } : {})), (config.searchToggle ? { searchToggle: config.searchToggle } : {})), (config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}));
39
+ return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (config.nav ? { nav: config.nav } : {})), (config.i18n ? { i18n: config.i18n } : {})), (config.githubUrl ? { githubUrl: config.githubUrl } : {})), (config.links ? { links: normalizeNavItems(config.links) } : {})), (config.searchToggle ? { searchToggle: config.searchToggle } : {}));
40
40
  }
41
41
 
42
42
  export { createSiteBaseLayoutConfig, createSiteNavGroup, createSiteNavLink, normalizeNavItems, toHomeLayoutOptions };
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type ThemeProviderProps } from 'next-themes';
3
+ import type { SiteThemeSwitchMode } from './site-layout-shared';
4
+ export interface SiteThemeProviderProps extends Omit<ThemeProviderProps, 'children'> {
5
+ mode?: SiteThemeSwitchMode;
6
+ children: ReactNode;
7
+ }
8
+ export declare function SiteThemeProvider({ mode, children, ...props }: SiteThemeProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,41 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var tslib = require('tslib');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var nextThemes = require('next-themes');
7
+
8
+ function SiteThemeProvider(_a) {
9
+ var { mode = 'light-dark-system', children } = _a, props = tslib.__rest(_a, ["mode", "children"]);
10
+ return (jsxRuntime.jsx(nextThemes.ThemeProvider, Object.assign({ attribute: "class", disableTransitionOnChange: true }, resolveThemeProviderProps(mode), props, { children: children })));
11
+ }
12
+ function resolveThemeProviderProps(mode) {
13
+ if (mode === 'light-only') {
14
+ return {
15
+ forcedTheme: 'light',
16
+ enableSystem: false,
17
+ defaultTheme: 'light',
18
+ };
19
+ }
20
+ if (mode === 'dark-only') {
21
+ return {
22
+ forcedTheme: 'dark',
23
+ enableSystem: false,
24
+ defaultTheme: 'dark',
25
+ };
26
+ }
27
+ if (mode === 'light-dark') {
28
+ return {
29
+ enableSystem: false,
30
+ defaultTheme: 'light',
31
+ forcedTheme: undefined,
32
+ };
33
+ }
34
+ return {
35
+ enableSystem: true,
36
+ defaultTheme: 'system',
37
+ forcedTheme: undefined,
38
+ };
39
+ }
40
+
41
+ exports.SiteThemeProvider = SiteThemeProvider;
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { __rest } from 'tslib';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import { ThemeProvider } from 'next-themes';
5
+
6
+ function SiteThemeProvider(_a) {
7
+ var { mode = 'light-dark-system', children } = _a, props = __rest(_a, ["mode", "children"]);
8
+ return (jsx(ThemeProvider, Object.assign({ attribute: "class", disableTransitionOnChange: true }, resolveThemeProviderProps(mode), props, { children: children })));
9
+ }
10
+ function resolveThemeProviderProps(mode) {
11
+ if (mode === 'light-only') {
12
+ return {
13
+ forcedTheme: 'light',
14
+ enableSystem: false,
15
+ defaultTheme: 'light',
16
+ };
17
+ }
18
+ if (mode === 'dark-only') {
19
+ return {
20
+ forcedTheme: 'dark',
21
+ enableSystem: false,
22
+ defaultTheme: 'dark',
23
+ };
24
+ }
25
+ if (mode === 'light-dark') {
26
+ return {
27
+ enableSystem: false,
28
+ defaultTheme: 'light',
29
+ forcedTheme: undefined,
30
+ };
31
+ }
32
+ return {
33
+ enableSystem: true,
34
+ defaultTheme: 'system',
35
+ forcedTheme: undefined,
36
+ };
37
+ }
38
+
39
+ export { SiteThemeProvider };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/third-ui",
3
- "version": "31.0.1",
3
+ "version": "31.1.0",
4
4
  "description": "Third-party integrated UI components for windrun-huaiin projects",
5
5
  "exports": {
6
6
  "./clerk": {
@@ -35,7 +35,11 @@ import { Popover, PopoverContent, PopoverTrigger } from 'fumadocs-ui/components/
35
35
  import { buttonVariants } from 'fumadocs-ui/components/ui/button';
36
36
  import { useI18n } from 'fumadocs-ui/contexts/i18n';
37
37
  import { HeaderThemeSwitch } from './header-theme-switch';
38
- import type { ExtendedLinkItem } from './site-layout-shared';
38
+ import type {
39
+ ExtendedLinkItem,
40
+ SiteThemeSwitchConfig,
41
+ SiteThemeSwitchMode,
42
+ } from './site-layout-shared';
39
43
 
40
44
  export type NavbarCSSVars = CSSProperties & {
41
45
  '--fd-banner-height'?: string;
@@ -47,7 +51,8 @@ const PrefetchLinkItem = LinkItem as (
47
51
  props: ComponentProps<typeof LinkItem> & { prefetch?: boolean },
48
52
  ) => ReactNode;
49
53
 
50
- export interface CustomHomeHeaderProps extends HomeLayoutProps {
54
+ export interface CustomHomeHeaderProps
55
+ extends Omit<HomeLayoutProps, 'themeSwitch'> {
51
56
  /**
52
57
  * Banner height in rem units
53
58
  *
@@ -92,6 +97,7 @@ export interface CustomHomeHeaderProps extends HomeLayoutProps {
92
97
  * Control order of utilities inside the mobile dropdown.
93
98
  */
94
99
  mobileMenuActionsOrder?: MobileMenuAction[];
100
+ themeSwitch?: SiteThemeSwitchConfig;
95
101
  }
96
102
 
97
103
  export type DesktopAction =
@@ -175,8 +181,12 @@ export function CustomHomeHeader({
175
181
  ? searchToggle.components?.lg ?? null
176
182
  : null,
177
183
  theme:
178
- themeSwitch.enabled !== false
179
- ? themeSwitch.component ?? <HeaderThemeSwitch mode={themeSwitch?.mode} />
184
+ shouldShowThemeSwitch(themeSwitch?.mode)
185
+ ? (
186
+ <HeaderThemeSwitch
187
+ mode={normalizeThemeSwitchMode(themeSwitch?.mode)}
188
+ />
189
+ )
180
190
  : null,
181
191
  i18n: i18n ? (
182
192
  <CompactLanguageToggle>
@@ -238,8 +248,12 @@ export function CustomHomeHeader({
238
248
  </CompactLanguageToggle>
239
249
  ) : null,
240
250
  theme:
241
- themeSwitch.enabled !== false
242
- ? themeSwitch.component ?? <HeaderThemeSwitch mode={themeSwitch?.mode} />
251
+ shouldShowThemeSwitch(themeSwitch?.mode)
252
+ ? (
253
+ <HeaderThemeSwitch
254
+ mode={normalizeThemeSwitchMode(themeSwitch?.mode)}
255
+ />
256
+ )
243
257
  : null,
244
258
  };
245
259
  const shouldRenderMobileUtilities = mobileMenuActionsOrder.some(
@@ -356,6 +370,16 @@ export function CustomHomeHeader({
356
370
  );
357
371
  }
358
372
 
373
+ function shouldShowThemeSwitch(mode?: SiteThemeSwitchMode): boolean {
374
+ return mode === 'light-dark' || mode === 'light-dark-system' || mode == null;
375
+ }
376
+
377
+ function normalizeThemeSwitchMode(
378
+ mode?: SiteThemeSwitchMode,
379
+ ): 'light-dark' | 'light-dark-system' {
380
+ return mode === 'light-dark' ? 'light-dark' : 'light-dark-system';
381
+ }
382
+
359
383
  interface CustomNavbarProps extends ComponentProps<'div'> {
360
384
  bannerHeight?: number;
361
385
  headerHeight?: number;
@@ -3,13 +3,16 @@ import { HomeLayout, type HomeLayoutProps } from 'fumadocs-ui/layouts/home';
3
3
  import { FumaBannerSuit } from '../fuma-banner-suit';
4
4
  import { Footer } from '../../main/footer';
5
5
  import { GoToTop } from '../../main/go-to-top';
6
+ import { SiteThemeProvider } from './site-theme-provider';
6
7
  import {
7
8
  NavbarCSSVars,
8
9
  CustomHomeHeader,
9
10
  type DesktopAction,
10
11
  type MobileBarAction,
11
12
  type MobileMenuAction,
13
+ type CustomHomeHeaderProps,
12
14
  } from './custom-header';
15
+ import type { SiteThemeSwitchConfig } from './site-layout-shared';
13
16
 
14
17
  // - Set bannerHeight/headerHeight to the rem values expected by the project. Use bannerHeight = 0 when there is no banner.
15
18
  // - layoutStyle passes the variables to HomeLayout's main element, offsetting content without has-banner/no-banner classes.
@@ -91,6 +94,10 @@ export interface CustomHomeLayoutProps {
91
94
  * The default locale for the application (default: 'en')
92
95
  */
93
96
  defaultLocale?: string;
97
+ /**
98
+ * Theme mode for this layout group.
99
+ */
100
+ themeSwitch?: SiteThemeSwitchConfig;
94
101
  children?: ReactNode;
95
102
  }
96
103
 
@@ -119,6 +126,7 @@ export function CustomHomeLayout({
119
126
  actionOrders,
120
127
  localePrefixAsNeeded = true,
121
128
  defaultLocale = 'en',
129
+ themeSwitch,
122
130
  }: CustomHomeLayoutProps) {
123
131
  const resolvedBannerHeight = bannerHeight ?? (showBanner ? 3 : 0.5);
124
132
  const resolvedPaddingTop =
@@ -139,6 +147,7 @@ export function CustomHomeLayout({
139
147
  <CustomHomeHeader
140
148
  {...homeLayoutProps}
141
149
  nav={navOptions}
150
+ themeSwitch={themeSwitch}
142
151
  bannerHeight={resolvedBannerHeight}
143
152
  headerHeight={headerHeight}
144
153
  navbarClassName={navbarClassName}
@@ -148,6 +157,7 @@ export function CustomHomeLayout({
148
157
  mobileMenuActionsOrder={actionOrders?.mobileMenu}
149
158
  />
150
159
  );
160
+ const themeMode = themeSwitch?.mode ?? 'light-dark-system';
151
161
 
152
162
  return (
153
163
  <>
@@ -158,19 +168,21 @@ export function CustomHomeLayout({
158
168
  floating={floatingNav}
159
169
  />
160
170
  )}
171
+ <SiteThemeProvider mode={themeMode}>
161
172
  <HomeLayout
162
- {...homeLayoutProps}
163
- nav={{
164
- ...navOptions,
165
- component: header,
166
- }}
167
- className='bg-neutral-100 dark:bg-neutral-900'
168
- style={layoutStyle}
169
- >
170
- {children}
171
- {showFooter ? footer ?? <Footer locale={locale} localePrefixAsNeeded={localePrefixAsNeeded} defaultLocale={defaultLocale} /> : null}
172
- {showGoToTop ? goToTop ?? <GoToTop /> : null}
173
- </HomeLayout>
173
+ {...homeLayoutProps}
174
+ nav={{
175
+ ...navOptions,
176
+ component: header,
177
+ }}
178
+ className='bg-neutral-100 dark:bg-neutral-900'
179
+ style={layoutStyle}
180
+ >
181
+ {children}
182
+ {showFooter ? footer ?? <Footer locale={locale} localePrefixAsNeeded={localePrefixAsNeeded} defaultLocale={defaultLocale} /> : null}
183
+ {showGoToTop ? goToTop ?? <GoToTop /> : null}
184
+ </HomeLayout>
185
+ </SiteThemeProvider>
174
186
  </>
175
187
  );
176
188
  }
@@ -1,58 +1,31 @@
1
1
  import type { ComponentProps, ReactNode } from 'react';
2
2
  import { NextProvider } from 'fumadocs-core/framework/next';
3
3
  import { I18nProvider, type I18nProviderProps } from 'fumadocs-ui/contexts/i18n';
4
- import { ThemeProvider, type ThemeProviderProps } from 'next-themes';
5
4
 
6
5
  type NextProviderComponents = {
7
6
  Link?: ComponentProps<typeof NextProvider>['Link'];
8
7
  Image?: ComponentProps<typeof NextProvider>['Image'];
9
8
  };
10
9
 
11
- type ThemeOptions = ThemeProviderProps & {
12
- enabled?: boolean;
13
- };
14
-
15
10
  export interface DocsRootProviderProps {
16
11
  i18n: Omit<I18nProviderProps, 'children'>;
17
- theme?: ThemeOptions;
18
12
  components?: NextProviderComponents;
19
13
  children: ReactNode;
20
14
  }
21
15
 
22
16
  export function DocsRootProvider({
23
17
  i18n,
24
- theme = {},
25
18
  components,
26
19
  children,
27
20
  }: DocsRootProviderProps) {
28
- let body = children;
29
-
30
- if (theme.enabled !== false) {
31
- body = (
32
- <ThemeProvider
33
- attribute="class"
34
- defaultTheme="system"
35
- enableSystem
36
- disableTransitionOnChange
37
- {...theme}
38
- >
39
- {body}
40
- </ThemeProvider>
41
- );
42
- }
43
-
44
- body = (
45
- <I18nProvider {...i18n}>
46
- {body}
47
- </I18nProvider>
48
- );
49
-
50
21
  return (
51
22
  <NextProvider
52
23
  Link={components?.Link}
53
24
  Image={components?.Image}
54
25
  >
55
- {body}
26
+ <I18nProvider {...i18n}>
27
+ {children}
28
+ </I18nProvider>
56
29
  </NextProvider>
57
30
  );
58
31
  }
@@ -5,6 +5,7 @@ import { AirplayIcon, MoonIcon, SunIcon } from '@windrun-huaiin/base-ui/icons';
5
5
  import { useTheme } from 'next-themes';
6
6
  import { type ComponentProps, useEffect, useState } from 'react';
7
7
  import { cn } from '@windrun-huaiin/lib/utils';
8
+ import type { SiteThemeSwitchMode } from './site-layout-shared';
8
9
 
9
10
  const itemVariants = cva('inline-flex size-6.5 items-center justify-center rounded-full p-1.5', {
10
11
  variants: {
@@ -18,7 +19,7 @@ const itemVariants = cva('inline-flex size-6.5 items-center justify-center round
18
19
  const full = [['light', SunIcon] as const, ['dark', MoonIcon] as const, ['system', AirplayIcon] as const];
19
20
 
20
21
  export interface HeaderThemeSwitchProps extends ComponentProps<'div'> {
21
- mode?: 'light-dark' | 'light-dark-system';
22
+ mode?: Exclude<SiteThemeSwitchMode, 'light-only' | 'dark-only'>;
22
23
  }
23
24
 
24
25
  export function HeaderThemeSwitch({
@@ -4,20 +4,36 @@ import {
4
4
  normalizeNavItems,
5
5
  type SiteBaseLayoutConfig,
6
6
  } from './site-layout-shared';
7
+ import { SiteThemeProvider } from './site-theme-provider';
7
8
 
8
9
  export interface SiteDocsLayoutConfig extends SiteBaseLayoutConfig {
9
10
  tree: DocsLayoutProps['tree'];
10
11
  sidebar?: DocsLayoutProps['sidebar'];
12
+ themeProvider?: boolean;
11
13
  }
12
14
 
13
15
  function toDocsLayoutOptions(config: SiteDocsLayoutConfig): DocsLayoutProps {
16
+ const themeMode = config.themeSwitch?.mode ?? 'light-dark-system';
17
+ const shouldShowThemeSwitch =
18
+ config.themeProvider !== false &&
19
+ (themeMode === 'light-dark' || themeMode === 'light-dark-system');
14
20
  return {
15
21
  ...(config.nav ? { nav: config.nav } : {}),
16
22
  ...(config.i18n ? { i18n: config.i18n } : {}),
17
23
  ...(config.githubUrl ? { githubUrl: config.githubUrl } : {}),
18
24
  ...(config.links ? { links: normalizeNavItems(config.links) } : {}),
19
25
  ...(config.searchToggle ? { searchToggle: config.searchToggle } : {}),
20
- ...(config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}),
26
+ ...(shouldShowThemeSwitch
27
+ ? {
28
+ themeSwitch: {
29
+ mode: themeMode,
30
+ },
31
+ }
32
+ : {
33
+ themeSwitch: {
34
+ enabled: false,
35
+ },
36
+ }),
21
37
  ...(config.sidebar ? { sidebar: config.sidebar } : {}),
22
38
  tree: config.tree,
23
39
  };
@@ -31,5 +47,10 @@ export function SiteDocsLayout({
31
47
  children: ReactNode;
32
48
  }) {
33
49
  const options = toDocsLayoutOptions(config);
34
- return <DocsLayout {...options}>{children}</DocsLayout>;
50
+ const themeMode = config.themeSwitch?.mode ?? 'light-dark-system';
51
+ const body = <DocsLayout {...options}>{children}</DocsLayout>;
52
+
53
+ if (config.themeProvider === false) return body;
54
+
55
+ return <SiteThemeProvider mode={themeMode}>{body}</SiteThemeProvider>;
35
56
  }
@@ -72,6 +72,7 @@ export function SiteHomeLayout({
72
72
  ...(showBanner != null ? { showBanner } : {}),
73
73
  ...(showFooter != null ? { showFooter } : {}),
74
74
  ...(showGoToTop != null ? { showGoToTop } : {}),
75
+ ...(config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}),
75
76
  };
76
77
 
77
78
  return <CustomHomeLayout {...layoutProps}>{children}</CustomHomeLayout>;
@@ -58,7 +58,17 @@ export interface SiteBaseLayoutConfig {
58
58
  githubUrl?: string;
59
59
  links?: SiteNavItemConfig[];
60
60
  searchToggle?: HomeLayoutProps['searchToggle'];
61
- themeSwitch?: HomeLayoutProps['themeSwitch'];
61
+ themeSwitch?: SiteThemeSwitchConfig;
62
+ }
63
+
64
+ export type SiteThemeSwitchMode =
65
+ | 'light-dark-system'
66
+ | 'light-dark'
67
+ | 'light-only'
68
+ | 'dark-only';
69
+
70
+ export interface SiteThemeSwitchConfig {
71
+ mode?: SiteThemeSwitchMode;
62
72
  }
63
73
 
64
74
  export interface SiteMenuLeafConfig {
@@ -185,6 +195,5 @@ export function toHomeLayoutOptions(config: SiteBaseLayoutConfig): HomeLayoutPro
185
195
  ...(config.githubUrl ? { githubUrl: config.githubUrl } : {}),
186
196
  ...(config.links ? { links: normalizeNavItems(config.links) } : {}),
187
197
  ...(config.searchToggle ? { searchToggle: config.searchToggle } : {}),
188
- ...(config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}),
189
198
  };
190
199
  }
@@ -0,0 +1,59 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+ import { ThemeProvider, type ThemeProviderProps } from 'next-themes';
5
+ import type { SiteThemeSwitchMode } from './site-layout-shared';
6
+
7
+ export interface SiteThemeProviderProps extends Omit<ThemeProviderProps, 'children'> {
8
+ mode?: SiteThemeSwitchMode;
9
+ children: ReactNode;
10
+ }
11
+
12
+ export function SiteThemeProvider({
13
+ mode = 'light-dark-system',
14
+ children,
15
+ ...props
16
+ }: SiteThemeProviderProps) {
17
+ return (
18
+ <ThemeProvider
19
+ attribute="class"
20
+ disableTransitionOnChange
21
+ {...resolveThemeProviderProps(mode)}
22
+ {...props}
23
+ >
24
+ {children}
25
+ </ThemeProvider>
26
+ );
27
+ }
28
+
29
+ function resolveThemeProviderProps(mode: SiteThemeSwitchMode): ThemeProviderProps {
30
+ if (mode === 'light-only') {
31
+ return {
32
+ forcedTheme: 'light',
33
+ enableSystem: false,
34
+ defaultTheme: 'light',
35
+ };
36
+ }
37
+
38
+ if (mode === 'dark-only') {
39
+ return {
40
+ forcedTheme: 'dark',
41
+ enableSystem: false,
42
+ defaultTheme: 'dark',
43
+ };
44
+ }
45
+
46
+ if (mode === 'light-dark') {
47
+ return {
48
+ enableSystem: false,
49
+ defaultTheme: 'light',
50
+ forcedTheme: undefined,
51
+ };
52
+ }
53
+
54
+ return {
55
+ enableSystem: true,
56
+ defaultTheme: 'system',
57
+ forcedTheme: undefined,
58
+ };
59
+ }