@fumadocs/base-ui 16.11.5 → 16.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/css/generated/docs.css +1 -1
  2. package/css/generated/flux.css +1 -1
  3. package/css/generated/glass.css +1 -0
  4. package/css/generated/home.css +1 -1
  5. package/css/generated/notebook.css +1 -1
  6. package/css/generated/shared.css +1 -1
  7. package/css/lib/base.css +2 -1
  8. package/css/preset.css +0 -1
  9. package/dist/.translations/index.d.ts +6 -0
  10. package/dist/.translations/keys.js +6 -0
  11. package/dist/components/image-zoom.d.ts +2 -2
  12. package/dist/components/image-zoom.js +1 -1
  13. package/dist/components/sidebar/tabs/dropdown.d.ts +2 -3
  14. package/dist/components/sidebar/tabs/dropdown.js +9 -4
  15. package/dist/components/tabs.js +0 -1
  16. package/dist/components/toc/index.js +1 -1
  17. package/dist/components/ui/popover.js +1 -1
  18. package/dist/components/ui/tabs.d.ts +1 -1
  19. package/dist/components/ui/tabs.js +36 -11
  20. package/dist/layouts/docs/client.js +10 -5
  21. package/dist/layouts/docs/page/slots/toc.js +17 -11
  22. package/dist/layouts/docs/slots/sidebar.js +8 -2
  23. package/dist/layouts/flux/index.d.ts +1 -1
  24. package/dist/layouts/flux/index.js +34 -34
  25. package/dist/layouts/flux/page/slots/toc.js +7 -4
  26. package/dist/layouts/flux/slots/sidebar.js +4 -4
  27. package/dist/layouts/flux/slots/tab-dropdown.js +8 -2
  28. package/dist/layouts/glass/index.d.ts +41 -0
  29. package/dist/layouts/glass/index.js +70 -0
  30. package/dist/layouts/glass/layout-tabs.js +53 -0
  31. package/dist/layouts/glass/page/index.d.ts +31 -0
  32. package/dist/layouts/glass/page/index.js +92 -0
  33. package/dist/layouts/glass/page/slots/breadcrumb.d.ts +7 -0
  34. package/dist/layouts/glass/page/slots/breadcrumb.js +44 -0
  35. package/dist/layouts/glass/page/slots/footer.d.ts +16 -0
  36. package/dist/layouts/glass/page/slots/footer.js +56 -0
  37. package/dist/layouts/glass/page/slots/toc.d.ts +19 -0
  38. package/dist/layouts/glass/page/slots/toc.js +108 -0
  39. package/dist/layouts/glass/slots/header.d.ts +6 -0
  40. package/dist/layouts/glass/slots/header.js +73 -0
  41. package/dist/layouts/glass/slots/sidebar.d.ts +23 -0
  42. package/dist/layouts/glass/slots/sidebar.js +294 -0
  43. package/dist/layouts/home/index.js +2 -2
  44. package/dist/layouts/home/slots/header.d.ts +1 -1
  45. package/dist/layouts/home/slots/header.js +1 -0
  46. package/dist/layouts/notebook/client.js +2 -2
  47. package/dist/layouts/notebook/page/slots/toc.js +17 -11
  48. package/dist/layouts/notebook/slots/header.js +8 -2
  49. package/dist/layouts/notebook/slots/sidebar.js +8 -2
  50. package/dist/layouts/shared/client.d.ts +1 -1
  51. package/dist/layouts/shared/client.js +1 -1
  52. package/dist/layouts/shared/index.d.ts +7 -6
  53. package/dist/layouts/shared/index.js +5 -3
  54. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/controlled.d.ts +1 -1
  55. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/controlled.js +1 -1
  56. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/uncontrolled.d.ts +1 -1
  57. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/uncontrolled.js +1 -1
  58. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/adjust-svg-ids.js +1 -1
  59. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/compute-positioned-style.js +1 -1
  60. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/element-tests.js +1 -1
  61. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-div-img-style.js +1 -1
  62. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-img-alt.js +1 -1
  63. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-img-object-fit-style.js +1 -1
  64. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-img-regular-style.js +1 -1
  65. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-img-src.js +1 -1
  66. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-modal-img-transform.js +1 -1
  67. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-scale.js +1 -1
  68. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-style-ghost.js +1 -1
  69. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-style-modal-img.js +1 -1
  70. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/get-target-dimension.js +1 -1
  71. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/parse-position.js +1 -1
  72. package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/utils/test-has-scalable-src.js +1 -1
  73. package/dist/style.css +985 -885
  74. package/package.json +16 -9
  75. /package/dist/node_modules/.pnpm/{react-medium-image-zoom@5.4.8_react-dom@19.2.7_react@19.2.7__react@19.2.7 → react-medium-image-zoom@5.4.8_react-dom@19.2.8_react@19.2.8__react@19.2.8}/node_modules/react-medium-image-zoom/dist/index.d.ts +0 -0
@@ -20,13 +20,13 @@ function useFluxLayout() {
20
20
  if (!context) throw new Error("Please use Flux layout components under <DocsLayout /> (`fumadocs-ui/layouts/flux`).");
21
21
  return context;
22
22
  }
23
- const { useProvider } = baseSlots({ useProps() {
23
+ const { useBaseSlots } = baseSlots({ useProps() {
24
24
  return useFluxLayout().props;
25
25
  } });
26
26
  function DocsLayout(props) {
27
27
  const { tree, nav = {}, sidebar: { enabled: sidebarEnabled = true, tabs: _tabs, defaultOpenLevel, prefetch, ...sidebarProps } = {}, tabs: defaultTabs = _tabs, children, containerProps, renderNavigationPanel = (props) => /* @__PURE__ */ jsx(NavigationPanel, { ...props }), slots: defaultSlots = {} } = props;
28
28
  const linkItems = useLinkItems(props);
29
- const { baseSlots, baseProps } = useProvider(props);
29
+ const { baseSlots, baseProps } = useBaseSlots(props);
30
30
  const tabs = useMemo(() => {
31
31
  if (Array.isArray(defaultTabs)) return defaultTabs;
32
32
  if (typeof defaultTabs === "object") return getLayoutTabs(tree, defaultTabs);
@@ -50,44 +50,44 @@ function DocsLayout(props) {
50
50
  slots,
51
51
  ...linkItems
52
52
  },
53
- children: /* @__PURE__ */ jsxs(TreeContextProvider, {
53
+ children: /* @__PURE__ */ jsx(TreeContextProvider, {
54
54
  tree,
55
- children: [/* @__PURE__ */ jsx(slots.sidebar.provider, {
55
+ children: /* @__PURE__ */ jsxs(slots.sidebar.provider, {
56
56
  defaultOpenLevel,
57
57
  prefetch,
58
- children: /* @__PURE__ */ jsxs(slots.container, {
58
+ children: [/* @__PURE__ */ jsxs(slots.container, {
59
59
  ...containerProps,
60
60
  children: [sidebarEnabled && /* @__PURE__ */ jsx(slots.sidebar.root, { ...sidebarProps }), children]
61
- })
62
- }), renderNavigationPanel({
63
- head: /* @__PURE__ */ jsxs(Fragment, { children: [slots.navTitle && /* @__PURE__ */ jsx(slots.navTitle, { className: "inline-flex items-center gap-2.5 text-sm font-semibold" }), nav.children] }),
64
- tabDropdown: slots.tabDropdown && tabs.length > 0 && /* @__PURE__ */ jsx(slots.tabDropdown, {
65
- className: "flex-1",
66
- tabs
67
- }),
68
- tool: /* @__PURE__ */ jsxs(Fragment, { children: [
69
- slots.languageSelect && /* @__PURE__ */ jsx(slots.languageSelect.root, { children: /* @__PURE__ */ jsx(Languages, { className: "size-4.5" }) }),
70
- slots.searchTrigger && /* @__PURE__ */ jsx(slots.searchTrigger.sm, {
71
- hideIfDisabled: true,
72
- className: "rounded-lg"
61
+ }), renderNavigationPanel({
62
+ head: /* @__PURE__ */ jsxs(Fragment, { children: [slots.navTitle && /* @__PURE__ */ jsx(slots.navTitle, { className: "inline-flex items-center gap-2.5 text-sm font-semibold" }), nav.children] }),
63
+ tabDropdown: slots.tabDropdown && tabs.length > 0 && /* @__PURE__ */ jsx(slots.tabDropdown, {
64
+ className: "flex-1",
65
+ tabs
73
66
  }),
74
- slots.sidebar && /* @__PURE__ */ jsx(slots.sidebar.trigger, { className: cn(buttonVariants({
75
- variant: "ghost",
76
- size: "icon-sm",
77
- className: "overflow-hidden"
78
- })) }),
79
- slots.themeSwitch && /* @__PURE__ */ jsx(slots.themeSwitch, { className: "p-1 h-full ms-1 rounded-xl bg-fd-muted *:rounded-lg" })
80
- ] }),
81
- link: linkItems.menuItems.filter((item) => item.type === "icon").map((item, i) => /* @__PURE__ */ jsx(LinkItem, {
82
- item,
83
- className: cn(buttonVariants({
84
- size: "icon-sm",
85
- color: "ghost"
86
- })),
87
- "aria-label": item.label,
88
- children: item.icon
89
- }, i))
90
- })]
67
+ tool: /* @__PURE__ */ jsxs(Fragment, { children: [
68
+ slots.languageSelect && /* @__PURE__ */ jsx(slots.languageSelect.root, { children: /* @__PURE__ */ jsx(Languages, { className: "size-4.5" }) }),
69
+ slots.searchTrigger && /* @__PURE__ */ jsx(slots.searchTrigger.sm, {
70
+ hideIfDisabled: true,
71
+ className: "rounded-lg"
72
+ }),
73
+ slots.sidebar && /* @__PURE__ */ jsx(slots.sidebar.trigger, { className: cn(buttonVariants({
74
+ variant: "ghost",
75
+ size: "icon-sm",
76
+ className: "overflow-hidden"
77
+ })) }),
78
+ slots.themeSwitch && /* @__PURE__ */ jsx(slots.themeSwitch, { className: "p-1 h-full ms-1 rounded-xl bg-fd-muted *:rounded-lg" })
79
+ ] }),
80
+ link: linkItems.menuItems.filter((item) => item.type === "icon").map((item, i) => /* @__PURE__ */ jsx(LinkItem, {
81
+ item,
82
+ className: cn(buttonVariants({
83
+ size: "icon-sm",
84
+ color: "ghost"
85
+ })),
86
+ "aria-label": item.label,
87
+ children: item.icon
88
+ }, i))
89
+ })]
90
+ })
91
91
  })
92
92
  });
93
93
  }
@@ -24,10 +24,13 @@ function TOC({ container, trigger, content, header, footer, style = "normal", li
24
24
  ...content,
25
25
  children: [
26
26
  header,
27
- /* @__PURE__ */ jsx(TOCScrollArea, { children: /* @__PURE__ */ jsxs(TOCItems, {
28
- ...list,
29
- children: [items.length === 0 && /* @__PURE__ */ jsx(TOCEmpty, {}), items.map((item) => /* @__PURE__ */ jsx(TOCItem, { item }, item.url))]
30
- }) }),
27
+ /* @__PURE__ */ jsx(TOCScrollArea, {
28
+ className: "ms-px",
29
+ children: /* @__PURE__ */ jsxs(TOCItems, {
30
+ ...list,
31
+ children: [items.length === 0 && /* @__PURE__ */ jsx(TOCEmpty, {}), items.map((item) => /* @__PURE__ */ jsx(TOCItem, { item }, item.url))]
32
+ })
33
+ }),
31
34
  footer
32
35
  ]
33
36
  }), /* @__PURE__ */ jsx(PageTOCPopoverTrigger, { ...trigger })]
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../../../utils/cn.js";
3
3
  import { mergeRefs } from "../../../utils/merge-refs.js";
4
- import { SidebarFolder as SidebarFolder$1, SidebarFolderContent as SidebarFolderContent$1, SidebarFolderLink as SidebarFolderLink$1, SidebarFolderTrigger as SidebarFolderTrigger$1, SidebarItem as SidebarItem$1, SidebarProvider as SidebarProvider$1, SidebarSeparator as SidebarSeparator$1, SidebarViewport, base_exports, useFolder, useFolderDepth } from "../../../components/sidebar/base.js";
4
+ import { SidebarFolder as SidebarFolder$1, SidebarFolderContent as SidebarFolderContent$1, SidebarFolderLink as SidebarFolderLink$1, SidebarFolderTrigger as SidebarFolderTrigger$1, SidebarItem as SidebarItem$1, SidebarProvider as SidebarProvider$1, SidebarSeparator as SidebarSeparator$1, base_exports, useFolder, useFolderDepth } from "../../../components/sidebar/base.js";
5
5
  import { createPageTreeRenderer } from "../../../components/sidebar/page-tree.js";
6
6
  import { createLinkItemRenderer } from "../../../components/sidebar/link-item.js";
7
7
  import { useFluxLayout } from "../index.js";
@@ -40,13 +40,13 @@ function Sidebar({ footer, banner, components, ...rest }) {
40
40
  className: "flex flex-col gap-3 p-4 pb-2 empty:hidden",
41
41
  children: banner
42
42
  }),
43
- /* @__PURE__ */ jsx(SidebarViewport, { children: /* @__PURE__ */ jsxs("div", {
44
- className: "flex flex-col",
43
+ /* @__PURE__ */ jsxs("div", {
44
+ className: "flex flex-col p-4",
45
45
  children: [menuItems.filter((v) => v.type !== "icon").map((item, i, list) => /* @__PURE__ */ jsx(SidebarLinkItem, {
46
46
  item,
47
47
  className: cn(i === list.length - 1 && "mb-4")
48
48
  }, i)), /* @__PURE__ */ jsx(SidebarPageTree, { ...components })]
49
- }) }),
49
+ }),
50
50
  footer
51
51
  ]
52
52
  });
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../../../utils/cn.js";
3
+ import { useTreePath } from "../../../contexts/tree.js";
3
4
  import { Popover, PopoverContent, PopoverTrigger } from "../../../components/ui/popover.js";
4
5
  import { isLayoutTabActive } from "../../shared/index.js";
5
6
  import Link from "fumadocs-core/link";
@@ -12,9 +13,14 @@ import { AnimatePresence, motion } from "motion/react";
12
13
  function TabDropdown({ tabs, placeholder, className, ...props }) {
13
14
  const [open, setOpen] = useState(false);
14
15
  const pathname = usePathname();
16
+ const path = useTreePath();
15
17
  const selectedIdx = useMemo(() => {
16
- return tabs.findLastIndex((item) => isLayoutTabActive(item, pathname));
17
- }, [tabs, pathname]);
18
+ return tabs.findLastIndex((item) => isLayoutTabActive(item, path, pathname));
19
+ }, [
20
+ tabs,
21
+ path,
22
+ pathname
23
+ ]);
18
24
  const selected = selectedIdx !== -1 ? tabs[selectedIdx] : void 0;
19
25
  const onClick = () => {
20
26
  setOpen(false);
@@ -0,0 +1,41 @@
1
+ import { BaseSlots, BaseSlotsProps } from "../shared/client.js";
2
+ import { BaseLayoutProps, GetLayoutTabsOptions, LayoutTab, LinkItemType } from "../shared/index.js";
3
+ import { SidebarDrawerProps, SidebarProps, SidebarProviderProps, useSidebar } from "./slots/sidebar.js";
4
+ import { HeaderProps } from "./slots/header.js";
5
+ import { FC } from "react";
6
+ import * as PageTree from "fumadocs-core/page-tree";
7
+ import { Drawer } from "@base-ui/react";
8
+ //#region src/layouts/glass/index.d.ts
9
+ interface GlassSlots extends BaseSlots {
10
+ header: FC<HeaderProps>;
11
+ sidebar: {
12
+ main: FC<SidebarProps>;
13
+ provider: FC<SidebarProviderProps>;
14
+ use: typeof useSidebar;
15
+ drawer: FC<SidebarDrawerProps>;
16
+ drawerHandle: Drawer.Handle<unknown>;
17
+ };
18
+ }
19
+ interface GlassLayoutProps extends BaseLayoutProps {
20
+ tree: PageTree.Root;
21
+ tabs?: LayoutTab[] | GetLayoutTabsOptions | false;
22
+ aiChat?: {
23
+ open: boolean;
24
+ onOpenChange: (v: boolean) => void;
25
+ };
26
+ slots?: Partial<GlassSlots>;
27
+ sidebar?: Omit<SidebarProviderProps, 'children'>;
28
+ }
29
+ interface SlotsProps extends BaseSlotsProps<GlassLayoutProps> {
30
+ tabs: LayoutTab[];
31
+ aiChat?: GlassLayoutProps['aiChat'];
32
+ }
33
+ declare function useGlassLayout(): {
34
+ props: SlotsProps;
35
+ navItems: LinkItemType[];
36
+ menuItems: LinkItemType[];
37
+ slots: GlassSlots;
38
+ };
39
+ declare function GlassLayout(props: GlassLayoutProps): import("react").JSX.Element;
40
+ //#endregion
41
+ export { GlassLayout, GlassLayoutProps, GlassSlots, useGlassLayout };
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { TreeContextProvider } from "../../contexts/tree.js";
3
+ import { baseSlots } from "../shared/client.js";
4
+ import { getLayoutTabs, useLinkItems } from "../shared/index.js";
5
+ import { Sidebar, SidebarDrawer, SidebarProvider, drawerHandle, useSidebar } from "./slots/sidebar.js";
6
+ import { Header } from "./slots/header.js";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { createContext, use, useMemo } from "react";
9
+ //#region src/layouts/glass/index.tsx
10
+ const LayoutContext = createContext(null);
11
+ function useGlassLayout() {
12
+ const context = use(LayoutContext);
13
+ if (!context) throw new Error("Please use Glass layout components under <GlassLayout /> (`fumadocs-ui/layouts/glass`).");
14
+ return context;
15
+ }
16
+ const { useBaseSlots } = baseSlots({ useProps() {
17
+ return useGlassLayout().props;
18
+ } });
19
+ function GlassLayout(props) {
20
+ const { tree, tabs: defaultTabs, aiChat, children, slots: defaultSlots = {} } = props;
21
+ const linkItems = useLinkItems(props);
22
+ const { baseSlots, baseProps } = useBaseSlots(props);
23
+ const tabs = useMemo(() => {
24
+ if (Array.isArray(defaultTabs)) return defaultTabs;
25
+ if (typeof defaultTabs === "object") return getLayoutTabs(tree, defaultTabs);
26
+ if (defaultTabs !== false) return getLayoutTabs(tree);
27
+ return [];
28
+ }, [tree, defaultTabs]);
29
+ const slots = {
30
+ ...baseSlots,
31
+ header: defaultSlots.header ?? Header,
32
+ sidebar: defaultSlots.sidebar ?? {
33
+ drawer: SidebarDrawer,
34
+ drawerHandle,
35
+ main: Sidebar,
36
+ provider: SidebarProvider,
37
+ use: useSidebar
38
+ }
39
+ };
40
+ return /* @__PURE__ */ jsx(LayoutContext, {
41
+ value: {
42
+ props: {
43
+ tabs,
44
+ aiChat,
45
+ ...baseProps
46
+ },
47
+ slots,
48
+ ...linkItems
49
+ },
50
+ children: /* @__PURE__ */ jsx(slots.sidebar.provider, {
51
+ ...props.sidebar,
52
+ children: /* @__PURE__ */ jsx(TreeContextProvider, {
53
+ tree,
54
+ children: /* @__PURE__ */ jsxs("div", {
55
+ id: "fd-glass-layout",
56
+ className: "grid overflow-x-clip min-h-dvh [--fd-main-width:900px] [--fd-left-width:0px] [--fd-right-width:0px]",
57
+ style: { gridTemplate: `"left left-margin main right-margin right" 1fr / var(--fd-left-width) calc(50% - var(--fd-main-width)/2 - var(--fd-left-width)) 1fr calc(50% - var(--fd-main-width)/2 - var(--fd-right-width)) var(--fd-right-width)` },
58
+ children: [
59
+ /* @__PURE__ */ jsx(slots.sidebar.drawer, {}),
60
+ /* @__PURE__ */ jsx(slots.sidebar.main, {}),
61
+ /* @__PURE__ */ jsx(slots.header, {}),
62
+ children
63
+ ]
64
+ })
65
+ })
66
+ })
67
+ });
68
+ }
69
+ //#endregion
70
+ export { GlassLayout, useGlassLayout };
@@ -0,0 +1,53 @@
1
+ import { cn } from "../../utils/cn.js";
2
+ import { useTreePath } from "../../contexts/tree.js";
3
+ import { Popover, PopoverContent, PopoverTrigger } from "../../components/ui/popover.js";
4
+ import { isLayoutTabActive } from "../shared/index.js";
5
+ import Link from "fumadocs-core/link";
6
+ import { usePathname } from "fumadocs-core/framework";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { ChevronsUpDown } from "lucide-react";
9
+ import { useState } from "react";
10
+ import { useTranslations } from "@fuma-translate/react";
11
+ //#region src/layouts/glass/layout-tabs.tsx
12
+ function LayoutTabsDropdown({ tabs, className, size = "default", ...props }) {
13
+ const path = useTreePath();
14
+ const pathname = usePathname();
15
+ const t = useTranslations();
16
+ const selected = tabs.findLast((t) => isLayoutTabActive(t, path, pathname));
17
+ const [open, setOpen] = useState(false);
18
+ return /* @__PURE__ */ jsxs(Popover, {
19
+ open,
20
+ onOpenChange: setOpen,
21
+ children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
22
+ className: cn("inline-flex items-center gap-2 text-sm font-medium rounded-full transition-colors hover:bg-fd-accent data-popup-open:bg-fd-accent", size === "lg" && "text-[0.9375rem]", className),
23
+ ...props,
24
+ children: [selected ? /* @__PURE__ */ jsxs(Fragment, { children: [selected.icon, /* @__PURE__ */ jsx("span", {
25
+ className: "truncate",
26
+ children: selected.title
27
+ })] }) : /* @__PURE__ */ jsx("span", {
28
+ className: "text-fd-muted-foreground truncate",
29
+ children: t("Layout Tab", { note: "layout tab trigger" })
30
+ }), /* @__PURE__ */ jsx(ChevronsUpDown, { className: cn("ms-auto shrink-0 text-fd-muted-foreground", size === "default" && "size-3.5!", size === "lg" && "size-4!") })]
31
+ }), /* @__PURE__ */ jsx(PopoverContent, {
32
+ className: "flex flex-col p-1 rounded-xl",
33
+ align: "start",
34
+ children: tabs.map((t, i) => {
35
+ if (t.unlisted && t !== selected) return;
36
+ return /* @__PURE__ */ jsxs(Link, {
37
+ href: t.url,
38
+ className: cn("text-sm px-2 py-1.5 rounded-lg", selected === t ? "bg-fd-primary/10 text-fd-primary" : "hover:bg-fd-accent hover:text-fd-accent-foreground"),
39
+ onClick: () => setOpen(false),
40
+ children: [/* @__PURE__ */ jsxs("div", {
41
+ className: "font-medium inline-flex items-center gap-2 [&_svg]:size-4",
42
+ children: [t.icon, t.title]
43
+ }), /* @__PURE__ */ jsx("p", {
44
+ className: cn("mt-1 text-xs text-fd-muted-foreground empty:hidden", selected === t && "text-fd-primary/80"),
45
+ children: t.description
46
+ })]
47
+ }, i);
48
+ })
49
+ })]
50
+ });
51
+ }
52
+ //#endregion
53
+ export { LayoutTabsDropdown };
@@ -0,0 +1,31 @@
1
+ import { MarkdownCopyButton, ViewOptionsPopover } from "../../shared/page-actions.js";
2
+ import { TOCProps } from "./slots/toc.js";
3
+ import { ComponentProps } from "react";
4
+ import { TOCItemType } from "fumadocs-core/toc";
5
+ //#region src/layouts/glass/page/index.d.ts
6
+ interface DocsPageProps extends ComponentProps<'article'> {
7
+ toc?: TOCItemType[];
8
+ /**
9
+ * Extend the page to fill all available space
10
+ *
11
+ * @defaultValue false
12
+ */
13
+ full?: boolean | undefined;
14
+ tableOfContent?: TOCProps;
15
+ }
16
+ declare function useDocsPage(): {
17
+ full: boolean;
18
+ };
19
+ declare function DocsPage({ full, toc, tableOfContent, children }: DocsPageProps): import("react").JSX.Element;
20
+ declare function EditOnGitHub(props: ComponentProps<'a'>): import("react").JSX.Element;
21
+ /**
22
+ * Add typography styles
23
+ */
24
+ declare function DocsBody({ children, className, ...props }: ComponentProps<'div'>): import("react").JSX.Element;
25
+ declare function DocsDescription({ children, className, ...props }: ComponentProps<'p'>): import("react").JSX.Element | null;
26
+ declare function DocsTitle({ children, className, ...props }: ComponentProps<'h1'>): import("react").JSX.Element;
27
+ declare function PageLastUpdate({ date: value, ...props }: Omit<ComponentProps<'p'>, 'children'> & {
28
+ date: Date;
29
+ }): import("react").JSX.Element;
30
+ //#endregion
31
+ export { DocsBody, DocsDescription, DocsPage, DocsPageProps, DocsTitle, EditOnGitHub, MarkdownCopyButton, PageLastUpdate, ViewOptionsPopover, useDocsPage };
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { cn } from "../../../utils/cn.js";
3
+ import { buttonVariants } from "../../../components/ui/button.js";
4
+ import { MarkdownCopyButton, ViewOptionsPopover } from "../../shared/page-actions.js";
5
+ import { Footer } from "./slots/footer.js";
6
+ import { Breadcrumb } from "./slots/breadcrumb.js";
7
+ import { TOC, TOCProvider } from "./slots/toc.js";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ import { Edit } from "lucide-react";
10
+ import { createContext, use, useEffect, useState } from "react";
11
+ import { useTranslations } from "@fuma-translate/react";
12
+ //#region src/layouts/glass/page/index.tsx
13
+ const PageContext = createContext(null);
14
+ function useDocsPage() {
15
+ const context = use(PageContext);
16
+ if (!context) throw new Error("Please use page components under <DocsPage /> (`fumadocs-ui/layouts/glass/page`).");
17
+ return context;
18
+ }
19
+ const Empty = [];
20
+ function DocsPage({ full = false, toc = Empty, tableOfContent, children }) {
21
+ return /* @__PURE__ */ jsx(PageContext, {
22
+ value: { full },
23
+ children: /* @__PURE__ */ jsxs(TOCProvider, {
24
+ toc,
25
+ children: [/* @__PURE__ */ jsxs("div", {
26
+ "data-fd-full": full,
27
+ className: cn("flex flex-col gap-2 p-6 pb-16 min-w-0 [grid-area:main] md:pt-16 md:pb-8", full && "layout:[--fd-main-width:1200px]"),
28
+ children: [
29
+ /* @__PURE__ */ jsx(Breadcrumb, {}),
30
+ children,
31
+ /* @__PURE__ */ jsx(Footer, {})
32
+ ]
33
+ }), /* @__PURE__ */ jsx(TOC, { ...tableOfContent })]
34
+ })
35
+ });
36
+ }
37
+ function EditOnGitHub(props) {
38
+ const t = useTranslations({ note: "edit page" });
39
+ return /* @__PURE__ */ jsx("a", {
40
+ target: "_blank",
41
+ rel: "noreferrer noopener",
42
+ ...props,
43
+ className: cn(buttonVariants({
44
+ color: "secondary",
45
+ size: "sm"
46
+ }), "gap-1.5 not-prose", props.className),
47
+ children: props.children ?? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Edit, { className: "size-3.5" }), t("Edit on GitHub")] })
48
+ });
49
+ }
50
+ /**
51
+ * Add typography styles
52
+ */
53
+ function DocsBody({ children, className, ...props }) {
54
+ return /* @__PURE__ */ jsx("div", {
55
+ ...props,
56
+ className: cn("prose flex-1", className),
57
+ children
58
+ });
59
+ }
60
+ function DocsDescription({ children, className, ...props }) {
61
+ if (children === void 0) return null;
62
+ return /* @__PURE__ */ jsx("p", {
63
+ ...props,
64
+ className: cn("mb-8 text-lg text-fd-muted-foreground", className),
65
+ children
66
+ });
67
+ }
68
+ function DocsTitle({ children, className, ...props }) {
69
+ return /* @__PURE__ */ jsx("h1", {
70
+ ...props,
71
+ className: cn("text-[1.75em] font-semibold", className),
72
+ children
73
+ });
74
+ }
75
+ function PageLastUpdate({ date: value, ...props }) {
76
+ const t = useTranslations({ note: "page footer" });
77
+ const [date, setDate] = useState("");
78
+ useEffect(() => {
79
+ setDate(value.toLocaleDateString());
80
+ }, [value]);
81
+ return /* @__PURE__ */ jsxs("p", {
82
+ ...props,
83
+ className: cn("text-sm text-fd-muted-foreground", props.className),
84
+ children: [
85
+ t("Last updated on"),
86
+ " ",
87
+ date
88
+ ]
89
+ });
90
+ }
91
+ //#endregion
92
+ export { DocsBody, DocsDescription, DocsPage, DocsTitle, EditOnGitHub, MarkdownCopyButton, PageLastUpdate, ViewOptionsPopover, useDocsPage };
@@ -0,0 +1,7 @@
1
+ import { ComponentProps } from "react";
2
+ import { BreadcrumbOptions } from "fumadocs-core/breadcrumb";
3
+ //#region src/layouts/glass/page/slots/breadcrumb.d.ts
4
+ type BreadcrumbProps = BreadcrumbOptions & ComponentProps<'div'>;
5
+ declare function Breadcrumb({ includeRoot, includeSeparator, includePage, ...props }: BreadcrumbProps): import("react").JSX.Element | null;
6
+ //#endregion
7
+ export { Breadcrumb, BreadcrumbProps };
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { cn } from "../../../../utils/cn.js";
3
+ import { useTreeContext, useTreePath } from "../../../../contexts/tree.js";
4
+ import Link from "fumadocs-core/link";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { ChevronRight } from "lucide-react";
7
+ import { Fragment as Fragment$1, useMemo } from "react";
8
+ import { getBreadcrumbItemsFromPath } from "fumadocs-core/breadcrumb";
9
+ //#region src/layouts/glass/page/slots/breadcrumb.tsx
10
+ function Breadcrumb({ includeRoot, includeSeparator, includePage, ...props }) {
11
+ const path = useTreePath();
12
+ const { root } = useTreeContext();
13
+ const items = useMemo(() => {
14
+ return getBreadcrumbItemsFromPath(root, path, {
15
+ includePage,
16
+ includeSeparator,
17
+ includeRoot
18
+ });
19
+ }, [
20
+ includePage,
21
+ includeRoot,
22
+ includeSeparator,
23
+ path,
24
+ root
25
+ ]);
26
+ if (items.length === 0) return null;
27
+ return /* @__PURE__ */ jsx("div", {
28
+ ...props,
29
+ className: cn("flex items-center gap-1.5 text-sm text-fd-muted-foreground", props.className),
30
+ children: items.map((item, i) => {
31
+ const className = cn("truncate", i === items.length - 1 && "text-fd-primary font-medium");
32
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [i !== 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5 shrink-0" }), item.url ? /* @__PURE__ */ jsx(Link, {
33
+ href: item.url,
34
+ className: cn(className, "transition-opacity hover:opacity-80"),
35
+ children: item.name
36
+ }) : /* @__PURE__ */ jsx("span", {
37
+ className,
38
+ children: item.name
39
+ })] }, i);
40
+ })
41
+ });
42
+ }
43
+ //#endregion
44
+ export { Breadcrumb };
@@ -0,0 +1,16 @@
1
+ import { ComponentProps } from "react";
2
+ import * as PageTree from "fumadocs-core/page-tree";
3
+ //#region src/layouts/glass/page/slots/footer.d.ts
4
+ type Item = Pick<PageTree.Item, 'name' | 'description' | 'url'>;
5
+ interface FooterProps extends ComponentProps<'div'> {
6
+ /**
7
+ * Items including information for the next and previous page
8
+ */
9
+ items?: {
10
+ previous?: Item;
11
+ next?: Item;
12
+ };
13
+ }
14
+ declare function Footer({ items, children, className, ...props }: FooterProps): import("react").JSX.Element;
15
+ //#endregion
16
+ export { Footer, FooterProps };
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { cn } from "../../../../utils/cn.js";
3
+ import { isActive } from "../../../../utils/urls.js";
4
+ import { useFooterItems } from "../../../../utils/use-footer-items.js";
5
+ import Link from "fumadocs-core/link";
6
+ import { usePathname } from "fumadocs-core/framework";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { ChevronLeft, ChevronRight } from "lucide-react";
9
+ import { useMemo } from "react";
10
+ import { useTranslations } from "@fuma-translate/react";
11
+ //#region src/layouts/glass/page/slots/footer.tsx
12
+ function Footer({ items, children, className, ...props }) {
13
+ const footerList = useFooterItems();
14
+ const pathname = usePathname();
15
+ const { previous, next } = useMemo(() => {
16
+ if (items) return items;
17
+ const idx = footerList.findIndex((item) => isActive(item.url, pathname));
18
+ if (idx === -1) return {};
19
+ return {
20
+ previous: footerList[idx - 1],
21
+ next: footerList[idx + 1]
22
+ };
23
+ }, [
24
+ footerList,
25
+ items,
26
+ pathname
27
+ ]);
28
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
29
+ className: cn("@container grid gap-4", previous && next ? "grid-cols-2" : "grid-cols-1", className),
30
+ ...props,
31
+ children: [previous && /* @__PURE__ */ jsx(FooterItem, {
32
+ item: previous,
33
+ index: 0
34
+ }), next && /* @__PURE__ */ jsx(FooterItem, {
35
+ item: next,
36
+ index: 1
37
+ })]
38
+ }), children] });
39
+ }
40
+ function FooterItem({ item, index }) {
41
+ const t = useTranslations({ note: "pagination" });
42
+ const Icon = index === 0 ? ChevronLeft : ChevronRight;
43
+ return /* @__PURE__ */ jsxs(Link, {
44
+ href: item.url,
45
+ className: cn("flex flex-col gap-2 rounded-lg border p-4 text-sm transition-colors hover:bg-fd-accent/80 hover:text-fd-accent-foreground @max-lg:col-span-full", index === 1 && "text-end"),
46
+ children: [/* @__PURE__ */ jsxs("div", {
47
+ className: cn("inline-flex items-center gap-1.5 font-medium", index === 1 && "flex-row-reverse"),
48
+ children: [/* @__PURE__ */ jsx(Icon, { className: "-mx-1 size-4 shrink-0 rtl:rotate-180" }), /* @__PURE__ */ jsx("p", { children: item.name })]
49
+ }), /* @__PURE__ */ jsx("p", {
50
+ className: "text-fd-muted-foreground truncate",
51
+ children: item.description ?? (index === 0 ? t("Previous Page") : t("Next Page"))
52
+ })]
53
+ });
54
+ }
55
+ //#endregion
56
+ export { Footer };
@@ -0,0 +1,19 @@
1
+ import { TOCProviderProps as TOCProviderProps$1 } from "../../../../components/toc/index.js";
2
+ import { ComponentProps, ReactNode } from "react";
3
+ //#region src/layouts/glass/page/slots/toc.d.ts
4
+ type TOCProviderProps = TOCProviderProps$1;
5
+ declare function TOCProvider(props: TOCProviderProps): import("react").JSX.Element;
6
+ interface TOCProps {
7
+ container?: ComponentProps<'div'>;
8
+ /**
9
+ * Custom content in TOC container, before the main TOC
10
+ */
11
+ header?: ReactNode;
12
+ /**
13
+ * Custom content in TOC container, after the main TOC
14
+ */
15
+ footer?: ReactNode;
16
+ }
17
+ declare function TOC({ container, header, footer }: TOCProps): import("react").JSX.Element | undefined;
18
+ //#endregion
19
+ export { TOC, TOCProps, TOCProvider, TOCProviderProps };