@windrun-huaiin/third-ui 21.0.0 → 22.0.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 (107) hide show
  1. package/dist/ai/ai-markdown.js +1 -1
  2. package/dist/ai/ai-markdown.mjs +1 -1
  3. package/dist/clerk/clerk-page-context-generator.js +3 -2
  4. package/dist/clerk/clerk-page-context-generator.mjs +3 -2
  5. package/dist/clerk/clerk-page-generator.js +4 -3
  6. package/dist/clerk/clerk-page-generator.mjs +4 -3
  7. package/dist/fuma/base/custom-header.d.ts +1 -1
  8. package/dist/fuma/base/custom-header.js +38 -36
  9. package/dist/fuma/base/custom-header.mjs +25 -23
  10. package/dist/fuma/base/custom-home-layout.d.ts +1 -1
  11. package/dist/fuma/base/custom-home-layout.js +1 -1
  12. package/dist/fuma/base/custom-home-layout.mjs +1 -1
  13. package/dist/fuma/base/header-theme-switch.d.ts +5 -0
  14. package/dist/fuma/base/header-theme-switch.js +42 -0
  15. package/dist/fuma/base/header-theme-switch.mjs +40 -0
  16. package/dist/fuma/base/index.d.ts +1 -0
  17. package/dist/fuma/base/index.js +7 -0
  18. package/dist/fuma/base/index.mjs +1 -0
  19. package/dist/fuma/base/site-layout.d.ts +116 -0
  20. package/dist/fuma/base/site-layout.js +72 -0
  21. package/dist/fuma/base/site-layout.mjs +65 -0
  22. package/dist/fuma/fuma-banner-suit.js +9 -6
  23. package/dist/fuma/fuma-banner-suit.mjs +10 -7
  24. package/dist/fuma/fuma-page-genarator.js +1 -1
  25. package/dist/fuma/fuma-page-genarator.mjs +1 -1
  26. package/dist/fuma/heavy/image-grid.d.ts +6 -0
  27. package/dist/fuma/heavy/image-grid.js +17 -0
  28. package/dist/fuma/heavy/image-grid.mjs +15 -0
  29. package/dist/fuma/heavy/image-zoom.d.ts +22 -0
  30. package/dist/fuma/heavy/image-zoom.js +39 -0
  31. package/dist/fuma/heavy/image-zoom.mjs +37 -0
  32. package/dist/fuma/heavy/index.d.ts +4 -0
  33. package/dist/fuma/heavy/index.js +15 -0
  34. package/dist/fuma/heavy/index.mjs +5 -0
  35. package/dist/fuma/heavy/math.d.ts +17 -0
  36. package/dist/fuma/heavy/math.js +60 -0
  37. package/dist/fuma/heavy/math.mjs +57 -0
  38. package/dist/fuma/heavy/mermaid.d.ts +13 -0
  39. package/dist/fuma/heavy/mermaid.js +360 -0
  40. package/dist/fuma/heavy/mermaid.mjs +358 -0
  41. package/dist/fuma/mdx/features.d.ts +8 -0
  42. package/dist/fuma/mdx/features.js +92 -0
  43. package/dist/fuma/mdx/features.mjs +85 -0
  44. package/dist/fuma/mdx/index.d.ts +0 -5
  45. package/dist/fuma/mdx/index.js +0 -11
  46. package/dist/fuma/mdx/index.mjs +0 -5
  47. package/dist/fuma/mdx/markdown-component-map.js +7 -1
  48. package/dist/fuma/mdx/markdown-component-map.mjs +7 -1
  49. package/dist/fuma/mdx/site-mdx-components.d.ts +13 -0
  50. package/dist/fuma/mdx/site-mdx-components.js +19 -0
  51. package/dist/fuma/mdx/site-mdx-components.mjs +17 -0
  52. package/dist/fuma/mdx/site-mdx-presets.d.ts +13 -0
  53. package/dist/fuma/mdx/site-mdx-presets.js +49 -0
  54. package/dist/fuma/mdx/site-mdx-presets.mjs +45 -0
  55. package/dist/fuma/mdx/toc-clerk-portable.js +9 -5
  56. package/dist/fuma/mdx/toc-clerk-portable.mjs +8 -4
  57. package/dist/fuma/mdx/zia-file.js +1 -0
  58. package/dist/fuma/mdx/zia-file.mjs +1 -0
  59. package/dist/fuma/server/optional-features.d.ts +8 -0
  60. package/dist/fuma/server/optional-features.js +111 -0
  61. package/dist/fuma/server/optional-features.mjs +104 -0
  62. package/dist/fuma/server/site-mdx-components.d.ts +13 -0
  63. package/dist/fuma/server/site-mdx-components.js +19 -0
  64. package/dist/fuma/server/site-mdx-components.mjs +17 -0
  65. package/dist/fuma/server/site-mdx-presets.d.ts +194 -0
  66. package/dist/fuma/server/site-mdx-presets.js +46 -0
  67. package/dist/fuma/server/site-mdx-presets.mjs +42 -0
  68. package/dist/fuma/share/index.d.ts +1 -0
  69. package/dist/fuma/share/index.js +7 -0
  70. package/dist/fuma/share/index.mjs +1 -0
  71. package/dist/fuma/share/markdown-component-map.d.ts +3 -0
  72. package/dist/fuma/share/markdown-component-map.js +79 -0
  73. package/dist/fuma/share/markdown-component-map.mjs +77 -0
  74. package/dist/lib/fuma-schema-check-util.js +19 -5
  75. package/dist/lib/fuma-schema-check-util.mjs +19 -5
  76. package/dist/lib/seo-metadata.d.ts +10 -0
  77. package/dist/main/x-button.js +2 -2
  78. package/dist/main/x-button.mjs +2 -2
  79. package/package.json +31 -8
  80. package/src/ai/ai-markdown.tsx +1 -1
  81. package/src/clerk/clerk-page-context-generator.tsx +6 -3
  82. package/src/clerk/clerk-page-generator.tsx +7 -4
  83. package/src/fuma/base/custom-header.tsx +32 -35
  84. package/src/fuma/base/custom-home-layout.tsx +2 -2
  85. package/src/fuma/base/header-theme-switch.tsx +88 -0
  86. package/src/fuma/base/index.ts +1 -0
  87. package/src/fuma/base/site-layout.tsx +289 -0
  88. package/src/fuma/fuma-banner-suit.tsx +30 -28
  89. package/src/fuma/fuma-page-genarator.tsx +1 -1
  90. package/src/fuma/{mdx → heavy}/image-grid.tsx +1 -1
  91. package/src/fuma/heavy/index.ts +7 -0
  92. package/src/fuma/mdx/index.ts +0 -5
  93. package/src/fuma/mdx/toc-clerk-portable.tsx +27 -24
  94. package/src/fuma/mdx/zia-file.tsx +3 -1
  95. package/src/fuma/server/optional-features.tsx +168 -0
  96. package/src/fuma/server/site-mdx-components.tsx +48 -0
  97. package/src/fuma/server/site-mdx-presets.ts +80 -0
  98. package/src/fuma/share/index.ts +1 -0
  99. package/src/fuma/{mdx → share}/markdown-component-map.tsx +1 -1
  100. package/src/lib/fuma-schema-check-util.ts +22 -6
  101. package/src/lib/seo-metadata.ts +47 -0
  102. package/src/main/x-button.tsx +2 -2
  103. package/src/styles/fuma.css +3 -7
  104. package/src/styles/third-ui.css +0 -4
  105. /package/src/fuma/{mdx → heavy}/image-zoom.tsx +0 -0
  106. /package/src/fuma/{mdx → heavy}/math.tsx +0 -0
  107. /package/src/fuma/{mdx → heavy}/mermaid.tsx +0 -0
@@ -5,28 +5,24 @@ import {
5
5
  type CSSProperties,
6
6
  Fragment,
7
7
  type ReactNode,
8
+ type FC,
8
9
  useMemo,
9
10
  useState,
10
11
  } from 'react';
11
12
  import { cva } from 'class-variance-authority';
12
13
  import { ChevronDownIcon, LanguagesIcon } from '@windrun-huaiin/base-ui/icons';
14
+ import { cn } from '@windrun-huaiin/lib/utils';
13
15
  import Link from 'fumadocs-core/link';
14
16
  import { HomeLayoutProps } from 'fumadocs-ui/layouts/home';
15
17
  import {
16
- BaseLinkItem,
18
+ LinkItem,
17
19
  type LinkItemType,
18
- getLinks,
20
+ resolveLinkItems,
19
21
  } from 'fumadocs-ui/layouts/shared';
20
- import { cn } from 'fumadocs-ui/utils/cn';
21
22
  import {
22
- LargeSearchToggle,
23
- SearchToggle,
24
- } from 'fumadocs-ui/components/layout/search-toggle';
25
- import { ThemeToggle } from 'fumadocs-ui/components/layout/theme-toggle';
26
- import {
27
- LanguageToggleText,
28
23
  type LanguageSelectProps,
29
- } from 'fumadocs-ui/components/layout/language-toggle';
24
+ LanguageSelectText,
25
+ } from 'fumadocs-ui/layouts/shared/slots/language-select';
30
26
  import {
31
27
  NavigationMenu,
32
28
  NavigationMenuContent,
@@ -38,12 +34,12 @@ import {
38
34
  } from 'fumadocs-ui/components/ui/navigation-menu';
39
35
  import { Popover, PopoverContent, PopoverTrigger } from 'fumadocs-ui/components/ui/popover';
40
36
  import { buttonVariants } from 'fumadocs-ui/components/ui/button';
41
- import { useNav } from 'fumadocs-ui/contexts/layout';
42
37
  import { useI18n } from 'fumadocs-ui/contexts/i18n';
38
+ import { HeaderThemeSwitch } from './header-theme-switch';
43
39
 
44
40
  export type NavbarCSSVars = CSSProperties & {
45
41
  '--fd-banner-height'?: string;
46
- '--fd-nav-height'?: string;
42
+ '--fd-header-height'?: string;
47
43
  '--fd-nav-max-width'?: string;
48
44
  };
49
45
 
@@ -143,7 +139,7 @@ export function CustomHomeHeader({
143
139
  mobileMenuActionsOrder = DEFAULT_MOBILE_MENU_ACTIONS,
144
140
  }: CustomHomeHeaderProps) {
145
141
  const finalLinks = useMemo(
146
- () => getLinks(links, githubUrl),
142
+ () => resolveLinkItems({ links, githubUrl }),
147
143
  [links, githubUrl],
148
144
  );
149
145
 
@@ -172,16 +168,11 @@ export function CustomHomeHeader({
172
168
  const desktopActionNodes: Record<DesktopAction, ReactNode> = {
173
169
  search:
174
170
  searchToggle.enabled !== false
175
- ? searchToggle.components?.lg ?? (
176
- <LargeSearchToggle
177
- className="w-full rounded-full ps-2.5 max-w-[240px]"
178
- hideIfDisabled
179
- />
180
- )
171
+ ? searchToggle.components?.lg ?? null
181
172
  : null,
182
173
  theme:
183
174
  themeSwitch.enabled !== false
184
- ? themeSwitch.component ?? <ThemeToggle mode={themeSwitch?.mode} />
175
+ ? themeSwitch.component ?? <HeaderThemeSwitch mode={themeSwitch?.mode} />
185
176
  : null,
186
177
  i18n: i18n ? (
187
178
  <CompactLanguageToggle>
@@ -238,13 +229,13 @@ export function CustomHomeHeader({
238
229
  i18n: i18n ? (
239
230
  <CompactLanguageToggle>
240
231
  <LanguagesIcon className="size-5" />
241
- <LanguageToggleText />
232
+ <LanguageSelectText />
242
233
  <ChevronDownIcon className="size-3 text-fd-muted-foreground" />
243
234
  </CompactLanguageToggle>
244
235
  ) : null,
245
236
  theme:
246
237
  themeSwitch.enabled !== false
247
- ? themeSwitch.component ?? <ThemeToggle mode={themeSwitch?.mode} />
238
+ ? themeSwitch.component ?? <HeaderThemeSwitch mode={themeSwitch?.mode} />
248
239
  : null,
249
240
  };
250
241
  const shouldRenderMobileUtilities = mobileMenuActionsOrder.some(
@@ -305,9 +296,7 @@ export function CustomHomeHeader({
305
296
  ) : null;
306
297
  const mobileSearchNode =
307
298
  searchToggle.enabled !== false
308
- ? searchToggle.components?.sm ?? (
309
- <SearchToggle className="p-2" hideIfDisabled />
310
- )
299
+ ? searchToggle.components?.sm ?? null
311
300
  : null;
312
301
  const mobileBarNodes: Record<MobileBarAction, ReactNode> = {
313
302
  pinned: mobilePinnedNode,
@@ -329,7 +318,7 @@ export function CustomHomeHeader({
329
318
  href={nav.url ?? '/'}
330
319
  className="inline-flex items-center gap-2.5 font-semibold"
331
320
  >
332
- {nav.title}
321
+ {renderNavTitle(nav.title)}
333
322
  </Link>
334
323
  {nav.children}
335
324
  <ul className="flex flex-row items-center gap-2 px-6 max-sm:hidden">
@@ -379,11 +368,11 @@ function CustomNavbar({
379
368
  ...props
380
369
  }: CustomNavbarProps) {
381
370
  const [value, setValue] = useState('');
382
- const { isTransparent } = useNav();
371
+ const isTransparent = false;
383
372
 
384
373
  const cssVars: NavbarCSSVars = {
385
374
  '--fd-banner-height': `${bannerHeight}rem`,
386
- '--fd-nav-height': `${headerHeight}rem`,
375
+ '--fd-header-height': `${headerHeight}rem`,
387
376
  ...(maxContentWidth
388
377
  ? {
389
378
  '--fd-nav-max-width':
@@ -401,9 +390,8 @@ function CustomNavbar({
401
390
  : maxContentWidth
402
391
  : '88rem';
403
392
  const minNavWidth = '15rem';
404
- const floatingWidth = `clamp(${minNavWidth}, 100vw, ${resolvedMaxWidth})`;
405
393
  const widthStyle = floating
406
- ? { width: floatingWidth }
394
+ ? { width: `clamp(${minNavWidth}, 100vw, ${resolvedMaxWidth})` }
407
395
  : { width: '100%', maxWidth: resolvedMaxWidth, minWidth: minNavWidth };
408
396
  const headerStyle = {
409
397
  ...cssVars,
@@ -431,7 +419,7 @@ function CustomNavbar({
431
419
  >
432
420
  <NavigationMenuList
433
421
  className="flex w-full items-center gap-4 px-1"
434
- style={{ height: 'var(--fd-nav-height)' }}
422
+ style={{ height: 'var(--fd-header-height)' }}
435
423
  asChild
436
424
  >
437
425
  <nav>{props.children}</nav>
@@ -535,7 +523,7 @@ function NavbarLinkItem({
535
523
  return (
536
524
  <NavigationMenuItem>
537
525
  <NavigationMenuLink asChild>
538
- <BaseLinkItem
526
+ <LinkItem
539
527
  item={item}
540
528
  aria-label={item.type === 'icon' ? item.label : undefined}
541
529
  {...props}
@@ -545,7 +533,7 @@ function NavbarLinkItem({
545
533
  )}
546
534
  >
547
535
  {item.type === 'icon' ? item.icon : item.text}
548
- </BaseLinkItem>
536
+ </LinkItem>
549
537
  </NavigationMenuLink>
550
538
  </NavigationMenuItem>
551
539
  );
@@ -593,7 +581,7 @@ function MenuLinkItem({
593
581
 
594
582
  return (
595
583
  <NavigationMenuLink asChild>
596
- <BaseLinkItem
584
+ <LinkItem
597
585
  item={item}
598
586
  className={cn(
599
587
  {
@@ -613,7 +601,7 @@ function MenuLinkItem({
613
601
  >
614
602
  {item.icon}
615
603
  {item.type === 'icon' ? undefined : item.text}
616
- </BaseLinkItem>
604
+ </LinkItem>
617
605
  </NavigationMenuLink>
618
606
  );
619
607
  }
@@ -719,3 +707,12 @@ function isSecondary(item: LinkItemType): boolean {
719
707
  function isMobilePinned(item: LinkItemType): boolean {
720
708
  return Boolean((item as { mobilePinned?: boolean }).mobilePinned);
721
709
  }
710
+
711
+ function renderNavTitle(title: unknown): ReactNode {
712
+ if (typeof title === 'function') {
713
+ const TitleComponent = title as FC<ComponentProps<'a'>>;
714
+ return <TitleComponent />;
715
+ }
716
+
717
+ return (title as ReactNode | null | undefined) ?? null;
718
+ }
@@ -1,6 +1,6 @@
1
1
  import type { CSSProperties, ReactNode } from 'react';
2
2
  import { HomeLayout, type HomeLayoutProps } from 'fumadocs-ui/layouts/home';
3
- import { type LinkItemType } from 'fumadocs-ui/layouts/docs';
3
+ import { type LinkItemType } from 'fumadocs-ui/layouts/shared';
4
4
  import { FumaBannerSuit } from '@third-ui/fuma/fuma-banner-suit';
5
5
  import { Footer } from '@third-ui/main/footer';
6
6
  import { GoToTop } from '@third-ui/main/go-to-top';
@@ -129,7 +129,7 @@ export function CustomHomeLayout({
129
129
 
130
130
  const layoutStyle: NavbarCSSVars = {
131
131
  '--fd-banner-height': `${resolvedBannerHeight}rem`,
132
- '--fd-nav-height': `${headerHeight}rem`,
132
+ '--fd-header-height': `${headerHeight}rem`,
133
133
  paddingTop: floatingNav
134
134
  ? `calc(var(--fd-banner-height) + ${resolvedPaddingTop}rem)`
135
135
  : `${resolvedPaddingTop}rem`,
@@ -0,0 +1,88 @@
1
+ 'use client';
2
+
3
+ import { cva } from 'class-variance-authority';
4
+ import { AirplayIcon, MoonIcon, SunIcon } from '@windrun-huaiin/base-ui/icons';
5
+ import { useTheme } from 'next-themes';
6
+ import { type ComponentProps, useEffect, useState } from 'react';
7
+ import { cn } from '@windrun-huaiin/lib/utils';
8
+
9
+ const itemVariants = cva('inline-flex size-6.5 items-center justify-center rounded-full p-1.5', {
10
+ variants: {
11
+ active: {
12
+ true: 'bg-fd-accent',
13
+ false: '',
14
+ },
15
+ },
16
+ });
17
+
18
+ const full = [['light', SunIcon] as const, ['dark', MoonIcon] as const, ['system', AirplayIcon] as const];
19
+
20
+ export interface HeaderThemeSwitchProps extends ComponentProps<'div'> {
21
+ mode?: 'light-dark' | 'light-dark-system';
22
+ }
23
+
24
+ export function HeaderThemeSwitch({
25
+ className,
26
+ mode = 'light-dark',
27
+ ...props
28
+ }: HeaderThemeSwitchProps) {
29
+ const { setTheme, theme, resolvedTheme } = useTheme();
30
+ const [mounted, setMounted] = useState(false);
31
+
32
+ useEffect(() => {
33
+ setMounted(true);
34
+ }, []);
35
+
36
+ const container = cn(
37
+ 'inline-flex items-center rounded-full border p-1 overflow-hidden *:rounded-full',
38
+ className,
39
+ );
40
+ const iconClassName = 'size-3.5 text-neutral-600 dark:text-neutral-300';
41
+
42
+ if (mode === 'light-dark') {
43
+ const value = mounted ? resolvedTheme : null;
44
+
45
+ return (
46
+ <button
47
+ type="button"
48
+ className={container}
49
+ aria-label="Toggle Theme"
50
+ onClick={() => setTheme(value === 'light' ? 'dark' : 'light')}
51
+ data-theme-toggle=""
52
+ >
53
+ {full.map(([key, Icon]) => {
54
+ if (key === 'system') return null;
55
+
56
+ return (
57
+ <Icon
58
+ key={key}
59
+ fill="currentColor"
60
+ className={cn(
61
+ itemVariants({ active: value === key }),
62
+ iconClassName,
63
+ )}
64
+ />
65
+ );
66
+ })}
67
+ </button>
68
+ );
69
+ }
70
+
71
+ const value = mounted ? theme : null;
72
+
73
+ return (
74
+ <div className={container} data-theme-toggle="" {...props}>
75
+ {full.map(([key, Icon]) => (
76
+ <button
77
+ key={key}
78
+ type="button"
79
+ aria-label={key}
80
+ className={cn(itemVariants({ active: value === key }))}
81
+ onClick={() => setTheme(key)}
82
+ >
83
+ <Icon className={iconClassName} fill="currentColor" />
84
+ </button>
85
+ ))}
86
+ </div>
87
+ );
88
+ }
@@ -1,2 +1,3 @@
1
1
  export * from './custom-header';
2
2
  export * from './custom-home-layout';
3
+ export * from './site-layout';
@@ -0,0 +1,289 @@
1
+ import { type ComponentProps, type HTMLAttributes, type ReactNode } from 'react';
2
+ import { RootProvider } from 'fumadocs-ui/provider/next';
3
+ import { DocsLayout, type DocsLayoutProps } from 'fumadocs-ui/layouts/docs';
4
+ import { type HomeLayoutProps } from 'fumadocs-ui/layouts/home';
5
+ import { CustomHomeLayout, type CustomHomeLayoutProps, type ExtendedLinkItem, type HeaderActionOrders } from './custom-home-layout';
6
+
7
+ type RootProviderI18n = NonNullable<ComponentProps<typeof RootProvider>['i18n']>;
8
+
9
+ export interface DocsRootProviderProps {
10
+ i18n: RootProviderI18n;
11
+ children: ReactNode;
12
+ }
13
+
14
+ type SiteMenuConfig = HTMLAttributes<HTMLElement> & {
15
+ banner?: ReactNode;
16
+ };
17
+
18
+ interface SiteNavSharedFields {
19
+ secondary?: boolean;
20
+ mobilePinned?: boolean;
21
+ }
22
+
23
+ export interface SiteNavLinkItemConfig extends SiteNavSharedFields {
24
+ type?: 'main' | 'icon' | 'button';
25
+ text: ReactNode;
26
+ url: string;
27
+ external?: boolean;
28
+ icon?: ReactNode;
29
+ description?: ReactNode;
30
+ menu?: SiteMenuConfig;
31
+ on?: 'nav' | 'menu' | 'all';
32
+ label?: string;
33
+ }
34
+
35
+ export interface SiteNavMenuItemConfig extends SiteNavSharedFields {
36
+ type: 'menu';
37
+ text: ReactNode;
38
+ url?: string;
39
+ external?: boolean;
40
+ icon?: ReactNode;
41
+ description?: ReactNode;
42
+ items: SiteNavItemConfig[];
43
+ menu?: SiteMenuConfig;
44
+ on?: 'nav' | 'menu' | 'all';
45
+ }
46
+
47
+ export interface SiteNavCustomItemConfig extends SiteNavSharedFields {
48
+ type: 'custom';
49
+ children: ReactNode;
50
+ }
51
+
52
+ export type SiteNavItemConfig =
53
+ | SiteNavLinkItemConfig
54
+ | SiteNavMenuItemConfig
55
+ | SiteNavCustomItemConfig;
56
+
57
+ export interface SiteBaseLayoutConfig {
58
+ nav?: HomeLayoutProps['nav'];
59
+ i18n?: HomeLayoutProps['i18n'];
60
+ githubUrl?: string;
61
+ links?: SiteNavItemConfig[];
62
+ searchToggle?: HomeLayoutProps['searchToggle'];
63
+ themeSwitch?: HomeLayoutProps['themeSwitch'];
64
+ }
65
+
66
+ export interface SiteHomeLayoutConfig extends SiteBaseLayoutConfig {
67
+ showBanner?: boolean;
68
+ bannerHeight?: number;
69
+ headerHeight?: number;
70
+ headerPaddingTop?: number;
71
+ navbarClassName?: string;
72
+ floatingNav?: boolean;
73
+ banner?: ReactNode;
74
+ footer?: ReactNode;
75
+ goToTop?: ReactNode;
76
+ showFooter?: boolean;
77
+ showGoToTop?: boolean;
78
+ actionOrders?: HeaderActionOrders;
79
+ localePrefixAsNeeded?: boolean;
80
+ defaultLocale?: string;
81
+ }
82
+
83
+ export interface SiteDocsLayoutConfig extends SiteBaseLayoutConfig {
84
+ tree: DocsLayoutProps['tree'];
85
+ sidebar?: DocsLayoutProps['sidebar'];
86
+ }
87
+
88
+ export interface SiteMenuLeafConfig {
89
+ text: ReactNode;
90
+ path: string;
91
+ description?: ReactNode;
92
+ icon?: ReactNode;
93
+ className?: string;
94
+ external?: boolean;
95
+ }
96
+
97
+ export interface SiteMenuGroupConfig {
98
+ text: ReactNode;
99
+ path?: string;
100
+ landing?: SiteMenuLeafConfig;
101
+ items: SiteMenuLeafConfig[];
102
+ }
103
+
104
+ export interface CreateSiteNavItemContext {
105
+ resolveUrl: (path: string) => string;
106
+ }
107
+
108
+ export interface CreateSiteNavGroupOptions {
109
+ featuredClassName?: string;
110
+ featuredBanner?: ReactNode;
111
+ }
112
+
113
+ export interface CreateSiteBaseLayoutOptions {
114
+ homeUrl: string;
115
+ title: ReactNode;
116
+ i18n?: HomeLayoutProps['i18n'];
117
+ githubUrl?: string;
118
+ transparentMode?: HomeLayoutProps['nav'] extends infer T
119
+ ? T extends { transparentMode?: infer U }
120
+ ? U
121
+ : never
122
+ : never;
123
+ }
124
+
125
+ function normalizeNavItems(items?: SiteNavItemConfig[]): ExtendedLinkItem[] | undefined {
126
+ if (!items) return undefined;
127
+
128
+ return items.map((item) => {
129
+ if (item.type === 'menu') {
130
+ return {
131
+ ...item,
132
+ items: normalizeNavItems(item.items) ?? [],
133
+ } as ExtendedLinkItem;
134
+ }
135
+
136
+ return item as ExtendedLinkItem;
137
+ });
138
+ }
139
+
140
+ export function createSiteNavLink(
141
+ item: SiteMenuLeafConfig,
142
+ context: CreateSiteNavItemContext,
143
+ ): SiteNavLinkItemConfig {
144
+ return {
145
+ type: 'main',
146
+ text: item.text,
147
+ ...(item.description ? { description: item.description } : {}),
148
+ url: context.resolveUrl(item.path),
149
+ ...(item.external ? { external: item.external } : {}),
150
+ ...(item.icon || item.className
151
+ ? {
152
+ menu: {
153
+ ...(item.icon ? { banner: item.icon } : {}),
154
+ ...(item.className ? { className: item.className } : {}),
155
+ },
156
+ }
157
+ : {}),
158
+ };
159
+ }
160
+
161
+ export function createSiteNavGroup(
162
+ item: SiteMenuGroupConfig,
163
+ context: CreateSiteNavItemContext,
164
+ options?: CreateSiteNavGroupOptions,
165
+ ): SiteNavMenuItemConfig {
166
+ return {
167
+ type: 'menu',
168
+ text: item.text,
169
+ ...(item.path ? { url: context.resolveUrl(item.path) } : {}),
170
+ items: [
171
+ ...(item.landing
172
+ ? [
173
+ {
174
+ ...createSiteNavLink(item.landing, context),
175
+ menu: {
176
+ ...(options?.featuredBanner ? { banner: options.featuredBanner } : {}),
177
+ className: options?.featuredClassName ?? 'md:row-span-2',
178
+ },
179
+ } satisfies SiteNavLinkItemConfig,
180
+ ]
181
+ : []),
182
+ ...item.items.map((child) => createSiteNavLink(child, context)),
183
+ ],
184
+ };
185
+ }
186
+
187
+ export function createSiteBaseLayoutConfig(
188
+ options: CreateSiteBaseLayoutOptions,
189
+ ): SiteBaseLayoutConfig {
190
+ return {
191
+ nav: {
192
+ url: options.homeUrl,
193
+ title: options.title,
194
+ transparentMode: options.transparentMode ?? 'none',
195
+ },
196
+ ...(options.i18n ? { i18n: options.i18n } : {}),
197
+ ...(options.githubUrl ? { githubUrl: options.githubUrl } : {}),
198
+ };
199
+ }
200
+
201
+ function toHomeLayoutOptions(config: SiteBaseLayoutConfig): HomeLayoutProps {
202
+ return {
203
+ ...(config.nav ? { nav: config.nav } : {}),
204
+ ...(config.i18n ? { i18n: config.i18n } : {}),
205
+ ...(config.githubUrl ? { githubUrl: config.githubUrl } : {}),
206
+ ...(config.links ? { links: normalizeNavItems(config.links) } : {}),
207
+ ...(config.searchToggle ? { searchToggle: config.searchToggle } : {}),
208
+ ...(config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}),
209
+ };
210
+ }
211
+
212
+ function toDocsLayoutOptions(config: SiteDocsLayoutConfig): DocsLayoutProps {
213
+ return {
214
+ ...(config.nav ? { nav: config.nav } : {}),
215
+ ...(config.i18n ? { i18n: config.i18n } : {}),
216
+ ...(config.githubUrl ? { githubUrl: config.githubUrl } : {}),
217
+ ...(config.links ? { links: normalizeNavItems(config.links) } : {}),
218
+ ...(config.searchToggle ? { searchToggle: config.searchToggle } : {}),
219
+ ...(config.themeSwitch ? { themeSwitch: config.themeSwitch } : {}),
220
+ ...(config.sidebar ? { sidebar: config.sidebar } : {}),
221
+ tree: config.tree,
222
+ };
223
+ }
224
+
225
+ export function DocsRootProvider({ i18n, children }: DocsRootProviderProps) {
226
+ return <RootProvider i18n={i18n}>{children}</RootProvider>;
227
+ }
228
+
229
+ export function SiteHomeLayout({
230
+ locale,
231
+ config,
232
+ children,
233
+ }: {
234
+ locale: string;
235
+ config: SiteHomeLayoutConfig;
236
+ children: ReactNode;
237
+ }) {
238
+ const {
239
+ actionOrders,
240
+ banner,
241
+ bannerHeight,
242
+ defaultLocale,
243
+ floatingNav,
244
+ footer,
245
+ goToTop,
246
+ headerHeight,
247
+ headerPaddingTop,
248
+ localePrefixAsNeeded,
249
+ navbarClassName,
250
+ showBanner,
251
+ showFooter,
252
+ showGoToTop,
253
+ ...baseConfig
254
+ } = config;
255
+
256
+ const options = toHomeLayoutOptions(baseConfig);
257
+
258
+ const layoutProps: CustomHomeLayoutProps = {
259
+ locale,
260
+ options,
261
+ ...(actionOrders ? { actionOrders } : {}),
262
+ ...(banner ? { banner } : {}),
263
+ ...(bannerHeight != null ? { bannerHeight } : {}),
264
+ ...(defaultLocale ? { defaultLocale } : {}),
265
+ ...(floatingNav != null ? { floatingNav } : {}),
266
+ ...(footer ? { footer } : {}),
267
+ ...(goToTop ? { goToTop } : {}),
268
+ ...(headerHeight != null ? { headerHeight } : {}),
269
+ ...(headerPaddingTop != null ? { headerPaddingTop } : {}),
270
+ ...(localePrefixAsNeeded != null ? { localePrefixAsNeeded } : {}),
271
+ ...(navbarClassName ? { navbarClassName } : {}),
272
+ ...(showBanner != null ? { showBanner } : {}),
273
+ ...(showFooter != null ? { showFooter } : {}),
274
+ ...(showGoToTop != null ? { showGoToTop } : {}),
275
+ };
276
+
277
+ return <CustomHomeLayout {...layoutProps}>{children}</CustomHomeLayout>;
278
+ }
279
+
280
+ export function SiteDocsLayout({
281
+ config,
282
+ children,
283
+ }: {
284
+ config: SiteDocsLayoutConfig;
285
+ children: ReactNode;
286
+ }) {
287
+ const options = toDocsLayoutOptions(config);
288
+ return <DocsLayout {...options}>{children}</DocsLayout>;
289
+ }
@@ -10,36 +10,38 @@ export async function FumaBannerSuit({
10
10
  showBanner: boolean;
11
11
  floating?: boolean;
12
12
  }) {
13
- const t = await getTranslations({ locale, namespace: 'home' });
14
- const heightValue = showBanner ? 3 : 0.5;
13
+ const heightValue = 3;
15
14
  const height = `${heightValue}rem`;
15
+
16
+ if (!showBanner) {
17
+ return (
18
+ <div
19
+ aria-hidden="true"
20
+ className="m-0 rounded-none bg-neutral-100 dark:bg-neutral-900"
21
+ style={{
22
+ position: floating ? 'fixed' : 'relative',
23
+ top: floating ? 0 : undefined,
24
+ left: floating ? 0 : undefined,
25
+ width: floating ? '100vw' : '100%',
26
+ zIndex: floating ? 1001 : undefined,
27
+ height,
28
+ minHeight: height,
29
+ maxHeight: height,
30
+ }}
31
+ />
32
+ );
33
+ }
34
+
35
+ const t = await getTranslations({ locale, namespace: 'home' });
16
36
  const bannerText = t('banner');
17
37
  return (
18
- <>
19
- {showBanner ? (
20
- <Banner
21
- variant="rainbow"
22
- changeLayout={true}
23
- height={heightValue}
24
- floating={floating}
25
- >
26
- <p className="text-sm sm:text-xl md:text-xl">{bannerText}</p>
27
- </Banner>
28
- ) : (
29
- <div
30
- className="m-0 rounded-none bg-neutral-100 dark:bg-neutral-900"
31
- style={{
32
- position: floating ? 'fixed' : 'relative',
33
- top: floating ? 0 : undefined,
34
- left: floating ? 0 : undefined,
35
- width: floating ? '100vw' : '100%',
36
- zIndex: floating ? 1001 : undefined,
37
- height: height,
38
- minHeight: height,
39
- maxHeight: height,
40
- }}
41
- />
42
- )}
43
- </>
38
+ <Banner
39
+ variant="rainbow"
40
+ changeLayout={true}
41
+ height={heightValue}
42
+ floating={floating}
43
+ >
44
+ <p className="text-sm sm:text-xl md:text-xl">{bannerText}</p>
45
+ </Banner>
44
46
  );
45
47
  }
@@ -137,7 +137,7 @@ export function createFumaPage({
137
137
  enabled: false,
138
138
  }}
139
139
  toc={content.toc}
140
- article={{ className: 'max-sm:pb-16' }}
140
+ className="max-sm:pb-16"
141
141
  >
142
142
  <DocsTitle>{page.data.title}</DocsTitle>
143
143
  <DocsDescription className="mb-2">{page.data.description}</DocsDescription>
@@ -32,4 +32,4 @@ export function ImageGrid({
32
32
  ))}
33
33
  </div>
34
34
  );
35
- }
35
+ }
@@ -0,0 +1,7 @@
1
+ 'use client';
2
+
3
+ export * from './image-zoom';
4
+ export * from './image-grid';
5
+ export * from './math';
6
+ export * from './mermaid';
7
+
@@ -1,9 +1,6 @@
1
1
  'use client';
2
2
 
3
- export * from './mermaid';
4
- export * from './image-zoom';
5
3
  export * from './trophy-card';
6
- export * from './image-grid';
7
4
  export * from './zia-card';
8
5
  export * from './gradient-button';
9
6
  export * from './toc-base';
@@ -13,5 +10,3 @@ export * from './toc-footer-wrapper';
13
10
  export * from './toc-clerk-portable';
14
11
  export * from './banner';
15
12
  export * from './suno-embed';
16
- export * from './markdown-component-map';
17
- export * from './math';