@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
@@ -0,0 +1,108 @@
1
+ "use client";
2
+ import { cn } from "../../../../utils/cn.js";
3
+ import { TOCProvider as TOCProvider$1, TOCScrollArea, useTOCItems } from "../../../../components/toc/index.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { Text } from "lucide-react";
6
+ import { createContext, use, useMemo, useRef, useState } from "react";
7
+ import { useTranslations } from "@fuma-translate/react";
8
+ import { TOCItem } from "fumadocs-core/toc";
9
+ import { useOnChange } from "fumadocs-core/utils/use-on-change";
10
+ //#region src/layouts/glass/page/slots/toc.tsx
11
+ const Context = createContext(null);
12
+ function TOCProvider(props) {
13
+ return /* @__PURE__ */ jsx(TOCProvider$1, { ...props });
14
+ }
15
+ function useContext$1() {
16
+ return use(Context);
17
+ }
18
+ function TOC({ container, header, footer }) {
19
+ const t = useTranslations({ note: "table of contents" });
20
+ const items = useTOCItems();
21
+ const [hover, setHover] = useState(false);
22
+ const [mobileOpen, setMobileOpen] = useState(false);
23
+ const [inAnimation, setInAnimation] = useState(false);
24
+ const exitTimerRef = useRef(null);
25
+ const transitionTimerRef = useRef(null);
26
+ const open = hover || mobileOpen;
27
+ useOnChange(open, () => {
28
+ setInAnimation(true);
29
+ if (transitionTimerRef.current) window.clearTimeout(transitionTimerRef.current);
30
+ transitionTimerRef.current = window.setTimeout(() => {
31
+ setInAnimation(false);
32
+ }, 300);
33
+ });
34
+ const ctx = useMemo(() => ({
35
+ open,
36
+ setMobileOpen,
37
+ inAnimation
38
+ }), [open, inAnimation]);
39
+ if (items.length === 0 && !footer && !header) return;
40
+ return /* @__PURE__ */ jsxs(Context, {
41
+ value: ctx,
42
+ children: [/* @__PURE__ */ jsx("div", {
43
+ className: cn("fixed inset-0 z-10 backdrop-blur-sm transition-opacity duration-300 [mask-image:radial-gradient(circle_at_center_right,white,white_200px,transparent_500px)] xl:hidden", !open && "opacity-0 pointer-events-none"),
44
+ onClick: () => setMobileOpen(false)
45
+ }), /* @__PURE__ */ jsx(TOCScrollArea, {
46
+ id: "nd-toc",
47
+ ...container,
48
+ className: cn("z-10 grid transition-[width,padding] duration-300 [grid-area:right]", "xl:sticky xl:top-10 xl:h-[calc(100dvh---spacing(10))] md:layout:[--fd-right-width:12px] xl:layout:[--fd-right-width:240px] xl:items-center xl:pe-4", "max-xl:fixed max-xl:top-1/2 max-xl:-translate-y-1/2 max-xl:end-1 max-xl:bg-fd-popover max-xl:text-fd-popover-foreground max-xl:border max-xl:rounded-xl max-xl:shadow-md max-xl:mask-none max-xl:max-h-[calc(100dvh---spacing(32))] max-xl:grid-cols-[calc(240px---spacing(6))]", inAnimation && "overflow-y-hidden", open ? "max-xl:w-[240px] max-xl:p-3" : "max-md:w-4 max-md:ps-[calc(--spacing(1.5)-1px)] max-xl:w-6 max-xl:ps-[calc(--spacing(2.5)-1px)] max-xl:overflow-clip", container?.className),
49
+ onPointerDown: (e) => {
50
+ if (!mobileOpen && (e.pointerType === "touch" || e.pointerType === "pen")) setMobileOpen(true);
51
+ },
52
+ onPointerEnter: (e) => {
53
+ if (e.pointerType === "mouse") {
54
+ if (exitTimerRef.current) window.clearTimeout(exitTimerRef.current);
55
+ if (!hover) setHover(true);
56
+ }
57
+ },
58
+ onPointerLeave: (e) => {
59
+ if (hover && e.pointerType === "mouse") {
60
+ if (exitTimerRef.current) window.clearTimeout(exitTimerRef.current);
61
+ exitTimerRef.current = window.setTimeout(() => {
62
+ setHover(false);
63
+ }, window.innerWidth - e.clientX < 30 ? 700 : 50);
64
+ }
65
+ },
66
+ children: /* @__PURE__ */ jsxs("div", {
67
+ className: "flex flex-col xl:items-end xl:text-end",
68
+ children: [
69
+ header,
70
+ /* @__PURE__ */ jsxs("h3", {
71
+ id: "nd-toc-title",
72
+ className: cn("inline-flex items-center gap-1.5 overflow-hidden text-xs text-fd-muted-foreground transition-[opacity,height] opacity-0 h-0", open ? "opacity-100 h-6" : "xl:[@media(hover:none)]:opacity-100 xl:[@media(hover:none)]:h-6"),
73
+ children: [/* @__PURE__ */ jsx(Text, { className: "size-3.5 shrink-0" }), t("On this page")]
74
+ }),
75
+ /* @__PURE__ */ jsx(TOCPanel, {}),
76
+ footer
77
+ ]
78
+ })
79
+ })]
80
+ });
81
+ }
82
+ function TOCPanel({ className, style, ...props }) {
83
+ const items = useTOCItems();
84
+ const { open, inAnimation, setMobileOpen } = useContext$1();
85
+ return /* @__PURE__ */ jsx("div", {
86
+ className: cn("grid grid-cols-1 transition-[grid-template-rows]", open ? "[--row:calc(6*var(--spacing))]" : "[--row:calc(3*var(--spacing))] xl:[@media(hover:none)]:[--row:calc(6*var(--spacing))]", (!open || inAnimation) && "pointer-events-none xl:[@media(hover:none)]:pointer-events-auto", className),
87
+ style: {
88
+ ...style,
89
+ gridTemplateRows: `repeat(${items.length}, var(--row))`
90
+ },
91
+ ...props,
92
+ children: items.map((item) => /* @__PURE__ */ jsxs(TOCItem, {
93
+ href: item.url,
94
+ className: "group prose prose-sm flex items-center gap-2 text-xs text-fd-muted-foreground transition-colors data-[active=true]:text-fd-primary data-[active=false]:hover:text-fd-accent-foreground xl:flex-row-reverse",
95
+ onClick: () => setMobileOpen(false),
96
+ autoScroll: open && !inAnimation,
97
+ children: [/* @__PURE__ */ jsx("div", {
98
+ className: cn("shrink-0 bg-fd-muted-foreground/50 rounded-full size-1 transition-[background-color,width,height] group-data-[active=true]:bg-fd-primary", open ? "w-(--width) h-px" : "transition-[width,height] duration-300 xl:[@media(hover:none)]:w-(--width) xl:[@media(hover:none)]:h-px"),
99
+ style: { "--width": `calc(pow(${item.depth}, 1.5) * var(--spacing))` }
100
+ }), /* @__PURE__ */ jsx("span", {
101
+ className: cn("truncate transition-opacity opacity-0", open ? "opacity-100" : "xl:[@media(hover:none)]:opacity-100"),
102
+ children: item.title
103
+ })]
104
+ }, item.url))
105
+ });
106
+ }
107
+ //#endregion
108
+ export { TOC, TOCProvider };
@@ -0,0 +1,6 @@
1
+ import { ComponentProps } from "react";
2
+ //#region src/layouts/glass/slots/header.d.ts
3
+ type HeaderProps = ComponentProps<'div'>;
4
+ declare function Header({ className, ...props }: HeaderProps): import("react").JSX.Element;
5
+ //#endregion
6
+ export { Header, HeaderProps };
@@ -0,0 +1,73 @@
1
+ "use client";
2
+ import { cn } from "../../../utils/cn.js";
3
+ import { buttonVariants } from "../../../components/ui/button.js";
4
+ import { LayoutTabsDropdown } from "../layout-tabs.js";
5
+ import { useGlassLayout } from "../index.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { ChevronsUpDown, LanguagesIcon, MessageCircleIcon, SidebarIcon } from "lucide-react";
8
+ import { useTranslations } from "@fuma-translate/react";
9
+ import { Drawer } from "@base-ui/react/drawer";
10
+ //#region src/layouts/glass/slots/header.tsx
11
+ const baseVariants = "rounded-full bg-fd-popover/80 text-fd-popover-foreground border backdrop-blur-sm shadow-sm";
12
+ function Header({ className, ...props }) {
13
+ const { props: { tabs, aiChat }, slots } = useGlassLayout();
14
+ const t = useTranslations();
15
+ const sidebar = slots.sidebar.use();
16
+ return /* @__PURE__ */ jsxs("div", {
17
+ className: cn("sticky flex flex-row justify-end gap-2 [grid-area:left-margin/left-margin/right/right] z-20 px-4 md:top-0 md:pt-2 md:px-2 md:h-12 md:bg-linear-to-b md:from-fd-background max-md:bottom-0 max-md:mt-auto max-md:h-16 max-md:pb-4 max-md:bg-linear-to-t max-md:from-fd-background", className),
18
+ ...props,
19
+ children: [
20
+ sidebar.collapsible && sidebar.collapsed && /* @__PURE__ */ jsx("button", {
21
+ "aria-label": t("Show Sidebar", { note: "sidebar" }),
22
+ className: cn(buttonVariants({
23
+ size: "icon-sm",
24
+ variant: "secondary"
25
+ }), baseVariants, "size-10 me-auto shrink-0 max-md:hidden"),
26
+ onClick: () => sidebar.setCollapsed(false),
27
+ children: /* @__PURE__ */ jsx(SidebarIcon, {})
28
+ }),
29
+ slots.searchTrigger && /* @__PURE__ */ jsx(slots.searchTrigger.sm, {
30
+ color: "secondary",
31
+ size: "icon",
32
+ className: cn(baseVariants, "size-12 shrink-0 md:hidden")
33
+ }),
34
+ tabs.length > 0 && /* @__PURE__ */ jsx(LayoutTabsDropdown, {
35
+ tabs,
36
+ size: "lg",
37
+ className: cn(baseVariants, "min-w-0 ps-4 pe-3 flex-1 [&_svg]:size-5 md:hidden")
38
+ }),
39
+ slots.searchTrigger && /* @__PURE__ */ jsx("div", {
40
+ className: "@container flex justify-end flex-1 max-md:hidden",
41
+ children: /* @__PURE__ */ jsx(slots.searchTrigger.full, { className: cn(baseVariants, "text-fd-muted-foreground ps-3 w-full @sm:max-w-[200px]") })
42
+ }),
43
+ aiChat && !aiChat.open && /* @__PURE__ */ jsxs("button", {
44
+ className: cn(buttonVariants({ variant: "secondary" }), baseVariants, "px-3 gap-2 text-fd-muted-foreground shrink-0 max-md:hidden"),
45
+ onClick: () => aiChat.onOpenChange(true),
46
+ children: [/* @__PURE__ */ jsx(MessageCircleIcon, { className: "size-4" }), t("Ask AI", { note: "AI chat button" })]
47
+ }),
48
+ slots.languageSelect && /* @__PURE__ */ jsxs(slots.languageSelect.root, {
49
+ className: cn(baseVariants, "px-3 rounded-full max-md:hidden"),
50
+ children: [
51
+ /* @__PURE__ */ jsx(LanguagesIcon, { className: "size-4 text-fd-muted-foreground shrink-0" }),
52
+ /* @__PURE__ */ jsx(slots.languageSelect.text, {}),
53
+ /* @__PURE__ */ jsx(ChevronsUpDown, { className: "size-3.5 text-fd-muted-foreground shrink-0" })
54
+ ]
55
+ }),
56
+ slots.themeSwitch && /* @__PURE__ */ jsx(slots.themeSwitch, { className: cn(baseVariants, "shrink-0 px-1.5 max-md:hidden") }),
57
+ /* @__PURE__ */ jsx(Drawer.Trigger, {
58
+ handle: slots.sidebar.drawerHandle,
59
+ render: (props, { open }) => /* @__PURE__ */ jsx("button", {
60
+ ...props,
61
+ className: cn(buttonVariants({
62
+ variant: "secondary",
63
+ size: "icon"
64
+ }), baseVariants, "shrink-0 size-12 md:hidden"),
65
+ "aria-label": open ? t("Close Sidebar", { note: "aria-label" }) : t("Open Sidebar", { note: "aria-label" }),
66
+ children: /* @__PURE__ */ jsx(SidebarIcon, {})
67
+ })
68
+ })
69
+ ]
70
+ });
71
+ }
72
+ //#endregion
73
+ export { Header };
@@ -0,0 +1,23 @@
1
+ import { ComponentProps, ReactNode } from "react";
2
+ import { Drawer } from "@base-ui/react/drawer";
3
+ //#region src/layouts/glass/slots/sidebar.d.ts
4
+ declare const drawerHandle: Drawer.Handle<unknown>;
5
+ interface SidebarProviderProps {
6
+ /** @default true */
7
+ collapsible?: boolean;
8
+ children: ReactNode;
9
+ }
10
+ declare function SidebarProvider({ children, collapsible }: SidebarProviderProps): import("react").JSX.Element;
11
+ declare function useSidebar(): {
12
+ collapsible: boolean;
13
+ collapsed: boolean;
14
+ setCollapsed: (v: boolean) => void;
15
+ };
16
+ interface SidebarDrawerProps {
17
+ contentProps?: ComponentProps<'div'>;
18
+ }
19
+ declare function SidebarDrawer({ contentProps }: SidebarDrawerProps): import("react").JSX.Element;
20
+ type SidebarProps = ComponentProps<'aside'>;
21
+ declare function Sidebar({ className, children, ...props }: SidebarProps): import("react").JSX.Element;
22
+ //#endregion
23
+ export { Sidebar, SidebarDrawer, SidebarDrawerProps, SidebarProps, SidebarProvider, SidebarProviderProps, drawerHandle, useSidebar };
@@ -0,0 +1,294 @@
1
+ "use client";
2
+ import { cn } from "../../../utils/cn.js";
3
+ import { buttonVariants } from "../../../components/ui/button.js";
4
+ import { useTreeContext, useTreePath } from "../../../contexts/tree.js";
5
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../components/ui/collapsible.js";
6
+ import { isLinkItemActive } from "../../shared/index.js";
7
+ import { ScrollArea, ScrollViewport } from "../../../components/ui/scroll-area.js";
8
+ import { LayoutTabsDropdown } from "../layout-tabs.js";
9
+ import { useGlassLayout } from "../index.js";
10
+ import Link from "fumadocs-core/link";
11
+ import { usePathname } from "fumadocs-core/framework";
12
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ import { ChevronDown, ChevronsUpDown, LanguagesIcon, MessageCircleIcon, SidebarIcon, XIcon } from "lucide-react";
14
+ import { cva } from "class-variance-authority";
15
+ import { cloneElement, createContext, use, useMemo, useState } from "react";
16
+ import { useTranslations } from "@fuma-translate/react";
17
+ import { useOnChange } from "fumadocs-core/utils/use-on-change";
18
+ import { Drawer } from "@base-ui/react/drawer";
19
+ //#region src/layouts/glass/slots/sidebar.tsx
20
+ const drawerHandle = Drawer.createHandle();
21
+ const itemTriggerVariants = cva("inline-flex items-center gap-2 rounded-lg px-2.5 py-2 md:py-1.5 [&_svg]:size-4 outline-none focus-visible:ring-2 focus-visible:ring-fd-ring", { variants: { active: {
22
+ true: "bg-fd-primary/10 text-fd-primary",
23
+ false: "text-fd-muted-foreground hover:bg-fd-accent hover:text-fd-accent-foreground"
24
+ } } });
25
+ const Context = createContext(null);
26
+ function SidebarProvider({ children, collapsible = true }) {
27
+ const [collapsed, setCollapsed] = useState(false);
28
+ return /* @__PURE__ */ jsx(Context, {
29
+ value: useMemo(() => ({
30
+ collapsible,
31
+ collapsed,
32
+ setCollapsed
33
+ }), [collapsible, collapsed]),
34
+ children
35
+ });
36
+ }
37
+ function useSidebar() {
38
+ const v = use(Context);
39
+ if (!v) throw new Error("Missing <SidebarProvider />");
40
+ return v;
41
+ }
42
+ function SidebarDrawer({ contentProps }) {
43
+ const { menuItems, props: { aiChat }, slots } = useGlassLayout();
44
+ const { root } = useTreeContext();
45
+ const t = useTranslations();
46
+ return /* @__PURE__ */ jsx(Drawer.Root, {
47
+ handle: drawerHandle,
48
+ swipeDirection: "right",
49
+ children: /* @__PURE__ */ jsxs(Drawer.Portal, {
50
+ className: "z-40",
51
+ children: [/* @__PURE__ */ jsx(Drawer.Backdrop, { className: "[--bleed:3rem] fixed inset-0 min-h-dvh bg-fd-overlay backdrop-blur-sm opacity-[calc(1-var(--drawer-swipe-progress))] transition-opacity duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] data-swiping:duration-0 data-ending-style:opacity-0 data-starting-style:opacity-0 data-ending-style:duration-[calc(var(--drawer-swipe-strength)*400ms)] supports-[-webkit-touch-callout:none]:absolute" }), /* @__PURE__ */ jsx(Drawer.Viewport, {
52
+ className: "[--viewport-padding:0px] supports-[-webkit-touch-callout:none]:[--viewport-padding:0.625rem] fixed inset-0 flex items-stretch justify-end p-(--viewport-padding)",
53
+ children: /* @__PURE__ */ jsx(Drawer.Popup, {
54
+ id: "nd-mobile-sidebar",
55
+ className: cn("relative [--bleed:3rem] supports-[-webkit-touch-callout:none]:[--bleed:0px] w-[360px] h-full max-w-[calc(100vw-3rem+var(--bleed))] pr-(--bleed) -mr-(--bleed) border-l bg-fd-background text-fd-foreground text-[0.9375rem] outline-none shadow-md touch-auto [transform:translateX(var(--drawer-swipe-movement-x))] transition-transform duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] data-swiping:select-none data-ending-style:[transform:translateX(calc(100%-var(--bleed)+var(--viewport-padding)+2px))] data-starting-style:[transform:translateX(calc(100%-var(--bleed)+var(--viewport-padding)+2px))] supports-[-webkit-touch-callout:none]:border supports-[-webkit-touch-callout:none]:rounded-xl", "[scrollbar-width:none] overflow-y-auto"),
56
+ children: /* @__PURE__ */ jsxs(Drawer.Content, {
57
+ ...contentProps,
58
+ className: cn("flex flex-col min-h-full px-3", contentProps?.className),
59
+ children: [
60
+ /* @__PURE__ */ jsxs("div", {
61
+ className: "sticky flex flex-col gap-2 top-0 pt-4 pb-2 bg-fd-background shadow-lg shadow-fd-background",
62
+ children: [/* @__PURE__ */ jsxs("div", {
63
+ className: "flex items-center gap-1.5 ps-2.5",
64
+ children: [
65
+ /* @__PURE__ */ jsx(Drawer.Title, { render: /* @__PURE__ */ jsx(slots.navTitle, { className: "flex items-center font-semibold gap-2 flex-1" }) }),
66
+ aiChat && /* @__PURE__ */ jsxs("button", {
67
+ className: cn(buttonVariants({
68
+ variant: "secondary",
69
+ size: "sm"
70
+ }), "rounded-full h-8 gap-1.5"),
71
+ onClick: () => {
72
+ aiChat.onOpenChange(!aiChat.open);
73
+ drawerHandle.close();
74
+ },
75
+ children: [/* @__PURE__ */ jsx(MessageCircleIcon, { className: "size-4 text-fd-muted-foreground" }), t("Ask AI", { note: "AI chat button" })]
76
+ }),
77
+ /* @__PURE__ */ jsx(Drawer.Close, {
78
+ "aria-label": t("Close Sidebar", { note: "aria-label" }),
79
+ className: cn(buttonVariants({
80
+ variant: "secondary",
81
+ size: "icon-sm"
82
+ }), "rounded-full"),
83
+ children: /* @__PURE__ */ jsx(XIcon, {})
84
+ })
85
+ ]
86
+ }), slots.languageSelect && /* @__PURE__ */ jsxs(slots.languageSelect.root, {
87
+ variant: "secondary",
88
+ className: "px-2.5 gap-2 rounded-lg",
89
+ children: [
90
+ /* @__PURE__ */ jsx(LanguagesIcon, { className: "size-4 text-fd-muted-foreground shrink-0" }),
91
+ /* @__PURE__ */ jsx(slots.languageSelect.text, {}),
92
+ /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ms-auto size-3.5 text-fd-muted-foreground shrink-0" })
93
+ ]
94
+ })]
95
+ }),
96
+ /* @__PURE__ */ jsxs("div", {
97
+ className: "flex flex-col py-2 flex-1",
98
+ children: [menuItems.map((item, i) => item.type !== "icon" && /* @__PURE__ */ jsx(SidebarLinkItem, { item }, i)), root.children.map((item, i) => cloneElement(renderNode(item), { key: i }))]
99
+ }),
100
+ /* @__PURE__ */ jsxs("div", {
101
+ className: "flex items-center sticky bottom-0 bg-fd-background px-1 pt-2 pb-4 border-t mt-2 empty:hidden",
102
+ children: [menuItems.map((item, i) => item.type === "icon" && /* @__PURE__ */ jsx(SidebarIconLinkItem, { item }, i)), slots.themeSwitch && /* @__PURE__ */ jsx(slots.themeSwitch, { className: "p-0 ms-auto" })]
103
+ })
104
+ ]
105
+ })
106
+ })
107
+ })]
108
+ })
109
+ });
110
+ }
111
+ function Sidebar({ className, children, ...props }) {
112
+ const { menuItems, props: { tabs }, slots } = useGlassLayout();
113
+ const { root } = useTreeContext();
114
+ const { collapsible, collapsed, setCollapsed } = useSidebar();
115
+ const t = useTranslations({ note: "sidebar" });
116
+ return /* @__PURE__ */ jsxs("aside", {
117
+ id: "nd-sidebar",
118
+ className: cn("sticky flex flex-col transition-transform [grid-area:left] my-2 ms-2 z-30 top-2 border rounded-2xl bg-fd-popover/80 text-fd-popover-foreground text-sm backdrop-blur-sm shadow-sm h-[calc(100dvh---spacing(4))] max-md:hidden md:layout:[--fd-left-width:280px]", collapsed && "w-[calc(280px---spacing(2))] -translate-x-[280px] md:layout:[--fd-left-width:0px]", className),
119
+ ...props,
120
+ children: [
121
+ /* @__PURE__ */ jsxs("div", {
122
+ className: "flex items-start gap-1 pt-4 px-3.5 empty:hidden",
123
+ children: [/* @__PURE__ */ jsx(slots.navTitle, { className: "flex text-sm items-center font-semibold gap-2 me-auto" }), /* @__PURE__ */ jsxs("div", {
124
+ className: "flex -mt-1.5 -me-1.5 empty:hidden",
125
+ children: [menuItems.map((item, i) => item.type === "icon" && /* @__PURE__ */ jsx(SidebarIconLinkItem, { item }, i)), collapsible && /* @__PURE__ */ jsx("button", {
126
+ "aria-label": collapsed ? t("Show Sidebar") : t("Hide Sidebar"),
127
+ className: cn(buttonVariants({
128
+ variant: "ghost",
129
+ size: "icon-sm"
130
+ }), "text-fd-muted-foreground"),
131
+ onClick: () => setCollapsed(!collapsed),
132
+ children: /* @__PURE__ */ jsx(SidebarIcon, {})
133
+ })]
134
+ })]
135
+ }),
136
+ tabs.length > 0 && /* @__PURE__ */ jsx(LayoutTabsDropdown, {
137
+ tabs,
138
+ className: "min-w-0 bg-fd-secondary text-fd-secondary-foreground rounded-xl px-2.5 py-2 [&_svg]:size-4 border rounded-xl shadow-sm mx-2 mt-2.5 empty:hidden"
139
+ }),
140
+ /* @__PURE__ */ jsx(ScrollArea, {
141
+ className: "min-h-0 flex-1",
142
+ children: /* @__PURE__ */ jsxs(ScrollViewport, {
143
+ className: "flex flex-col p-2 [mask-image:linear-gradient(to_bottom,transparent,white_16px,white_calc(100%-16px),transparent))]",
144
+ children: [menuItems.map((item, i) => item.type !== "icon" && /* @__PURE__ */ jsx(SidebarLinkItem, { item }, i)), root.children.map((item, i) => cloneElement(renderNode(item), { key: i }))]
145
+ })
146
+ }),
147
+ children
148
+ ]
149
+ });
150
+ }
151
+ function SidebarIconLinkItem({ item, className }) {
152
+ return /* @__PURE__ */ jsx(Link, {
153
+ href: item.url,
154
+ external: item.external,
155
+ "aria-label": item.label,
156
+ className: cn(buttonVariants({
157
+ variant: "ghost",
158
+ size: "icon-sm"
159
+ }), "text-fd-muted-foreground", className),
160
+ onClick: () => {
161
+ drawerHandle.close();
162
+ },
163
+ children: item.icon
164
+ });
165
+ }
166
+ function SidebarLinkItem({ item, className }) {
167
+ const pathname = usePathname();
168
+ const [open, setOpen] = useState(true);
169
+ switch (item.type) {
170
+ case "custom": return /* @__PURE__ */ jsx("div", {
171
+ className,
172
+ children: item.children
173
+ });
174
+ case "menu": {
175
+ const rightIcon = /* @__PURE__ */ jsx(ChevronDown, { className: cn("ms-auto text-fd-muted-foreground size-3.5! transition-transform", !open && "-rotate-90") });
176
+ return /* @__PURE__ */ jsxs(Collapsible, {
177
+ open,
178
+ onOpenChange: setOpen,
179
+ className: cn("mt-4 first:mt-0", className),
180
+ children: [item.url ? /* @__PURE__ */ jsxs("div", {
181
+ className: "flex w-full px-2.5 py-1.5 font-medium",
182
+ children: [/* @__PURE__ */ jsxs(Link, {
183
+ href: item.url,
184
+ external: item.external,
185
+ className: cn("inline-flex items-center gap-2 [&_svg]:size-4", isLinkItemActive(item, pathname) ? "text-fd-primary" : "hover:underline hover:decoration-fd-muted-foreground hover:underline-offset-4 hover:decoration-dashed hover:text-fd-accent-foreground"),
186
+ onClick: () => {
187
+ drawerHandle.close();
188
+ },
189
+ children: [item.icon, item.text]
190
+ }), /* @__PURE__ */ jsx(CollapsibleTrigger, {
191
+ className: "flex-1",
192
+ children: rightIcon
193
+ })]
194
+ }) : /* @__PURE__ */ jsxs(CollapsibleTrigger, {
195
+ className: "w-full px-2.5 py-1.5 font-medium inline-flex items-center gap-2 [&_svg]:size-4",
196
+ children: [
197
+ item.icon,
198
+ item.text,
199
+ rightIcon
200
+ ]
201
+ }), /* @__PURE__ */ jsx(CollapsibleContent, {
202
+ className: "flex flex-col",
203
+ children: item.items.map((item, i) => /* @__PURE__ */ jsx(SidebarLinkItem, { item }, i))
204
+ })]
205
+ });
206
+ }
207
+ default: return /* @__PURE__ */ jsxs(Link, {
208
+ href: item.url,
209
+ external: item.external,
210
+ className: cn(itemTriggerVariants({
211
+ active: isLinkItemActive(item, pathname),
212
+ className
213
+ })),
214
+ onClick: () => {
215
+ drawerHandle.close();
216
+ },
217
+ children: [item.icon, item.text]
218
+ });
219
+ }
220
+ }
221
+ function renderNode(node) {
222
+ if (node.type === "page") return /* @__PURE__ */ jsx(SidebarItem, { item: node });
223
+ if (node.type === "folder") return /* @__PURE__ */ jsx(SidebarFolder, { folder: node });
224
+ return /* @__PURE__ */ jsxs("p", {
225
+ className: "mt-4 w-full px-2.5 py-1.5 font-medium inline-flex items-center gap-2 [&_svg]:size-4 empty:hidden first:mt-0",
226
+ children: [node.icon, node.name]
227
+ });
228
+ }
229
+ function SidebarItem({ item }) {
230
+ const path = useTreePath();
231
+ return /* @__PURE__ */ jsxs(Link, {
232
+ href: item.url,
233
+ external: item.external,
234
+ className: itemTriggerVariants({ active: isNodeInPath(item, path) }),
235
+ onClick: () => {
236
+ drawerHandle.close();
237
+ },
238
+ children: [item.icon, item.name]
239
+ });
240
+ }
241
+ function SidebarFolder({ folder }) {
242
+ const path = useTreePath();
243
+ const shouldOpen = (folder.defaultOpen ?? true) || isNodeInPath(folder, path);
244
+ const [open, setOpen] = useState(shouldOpen);
245
+ useOnChange(shouldOpen, () => shouldOpen && setOpen(true));
246
+ if (folder.collapsible === false) return /* @__PURE__ */ jsxs(Fragment, { children: [folder.index ? /* @__PURE__ */ jsxs(Link, {
247
+ href: folder.index.url,
248
+ external: folder.index.external,
249
+ className: cn("inline-flex px-2.5 py-1.5 font-medium items-center gap-2 [&_svg]:size-4 mt-4 first:mt-0", isNodeInPath(folder.index, path) ? "text-fd-primary" : "hover:underline hover:decoration-fd-muted-foreground hover:underline-offset-4 hover:decoration-dashed hover:text-fd-accent-foreground"),
250
+ onClick: () => {
251
+ drawerHandle.close();
252
+ },
253
+ children: [folder.icon, folder.name]
254
+ }) : /* @__PURE__ */ jsxs("div", {
255
+ className: "w-full px-3 py-1.5 font-medium inline-flex items-center gap-2 [&_svg]:size-4 mt-4 first:mt-0",
256
+ children: [folder.icon, folder.name]
257
+ }), folder.children.map((item, i) => cloneElement(renderNode(item), { key: i }))] });
258
+ const rightIcon = /* @__PURE__ */ jsx(ChevronDown, { className: cn("ms-auto text-fd-muted-foreground size-3.5! transition-transform", !open && "-rotate-90") });
259
+ return /* @__PURE__ */ jsxs(Collapsible, {
260
+ open,
261
+ onOpenChange: setOpen,
262
+ className: "mt-4 first:mt-0",
263
+ children: [folder.index ? /* @__PURE__ */ jsxs("div", {
264
+ className: "flex w-full px-2.5 py-1.5 font-medium",
265
+ children: [/* @__PURE__ */ jsxs(Link, {
266
+ href: folder.index.url,
267
+ external: folder.index.external,
268
+ className: cn("inline-flex items-center gap-2 [&_svg]:size-4", isNodeInPath(folder.index, path) ? "text-fd-primary" : "hover:underline hover:decoration-fd-muted-foreground hover:underline-offset-4 hover:decoration-dashed hover:text-fd-accent-foreground"),
269
+ onClick: () => {
270
+ drawerHandle.close();
271
+ },
272
+ children: [folder.icon, folder.name]
273
+ }), /* @__PURE__ */ jsx(CollapsibleTrigger, {
274
+ className: "flex-1",
275
+ children: rightIcon
276
+ })]
277
+ }) : /* @__PURE__ */ jsxs(CollapsibleTrigger, {
278
+ className: "w-full px-2.5 py-1.5 font-medium inline-flex items-center gap-2 [&_svg]:size-4",
279
+ children: [
280
+ folder.icon,
281
+ folder.name,
282
+ rightIcon
283
+ ]
284
+ }), /* @__PURE__ */ jsx(CollapsibleContent, {
285
+ className: "flex flex-col",
286
+ children: folder.children.map((item, i) => cloneElement(renderNode(item), { key: i }))
287
+ })]
288
+ });
289
+ }
290
+ function isNodeInPath(node, path) {
291
+ return path.some((other) => other.$id === node.$id || other === node);
292
+ }
293
+ //#endregion
294
+ export { Sidebar, SidebarDrawer, SidebarProvider, drawerHandle, useSidebar };
@@ -12,12 +12,12 @@ function useHomeLayout() {
12
12
  if (!context) throw new Error("Please use this component under <HomeLayout /> (`fumadocs-ui/layouts/home`).");
13
13
  return context;
14
14
  }
15
- const { useProvider } = baseSlots({ useProps() {
15
+ const { useBaseSlots } = baseSlots({ useProps() {
16
16
  return useHomeLayout().props;
17
17
  } });
18
18
  function HomeLayout(props) {
19
19
  const { nav: { enabled: navEnabled = true } = {}, slots: defaultSlots, children, i18n: _i18n, githubUrl: _githubUrl, links: _links, themeSwitch: _themeSwitch, searchToggle: _searchToggle, ...rest } = props;
20
- const { baseSlots, baseProps } = useProvider(props);
20
+ const { baseSlots, baseProps } = useBaseSlots(props);
21
21
  const linkItems = useLinkItems(props);
22
22
  const slots = {
23
23
  ...baseSlots,
@@ -1,7 +1,7 @@
1
1
  import { ComponentProps } from "react";
2
2
  //#region src/layouts/home/slots/header.d.ts
3
3
  declare const navItemVariants: (props?: ({
4
- variant?: "main" | "icon" | "button" | null | undefined;
4
+ variant?: "button" | "main" | "icon" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  declare function Header(props: ComponentProps<'header'>): import("react").JSX.Element;
7
7
  //#endregion
@@ -52,6 +52,7 @@ function Header(props) {
52
52
  }, []);
53
53
  const list = /* @__PURE__ */ jsxs(NavigationMenu.List, {
54
54
  ref: listRef,
55
+ render: /* @__PURE__ */ jsx("div", {}),
55
56
  className: "flex h-14 w-full mx-auto max-w-(--fd-layout-width) items-center px-4",
56
57
  children: [
57
58
  slots.navTitle && /* @__PURE__ */ jsx(slots.navTitle, { className: "inline-flex items-center gap-2.5 font-semibold" }),
@@ -9,7 +9,7 @@ import { Header } from "./slots/header.js";
9
9
  import { jsx, jsxs } from "react/jsx-runtime";
10
10
  import { createContext, use } from "react";
11
11
  //#region src/layouts/notebook/client.tsx
12
- const { useProvider } = baseSlots({ useProps() {
12
+ const { useBaseSlots } = baseSlots({ useProps() {
13
13
  return useNotebookLayout().props;
14
14
  } });
15
15
  const LayoutContext = createContext(null);
@@ -22,7 +22,7 @@ function LayoutBody(props) {
22
22
  const { nav: { enabled: navEnabled = true, transparentMode: navTransparentMode = "none" } = {}, sidebar: { defaultOpenLevel, prefetch, ...sidebarProps } = {}, slots: defaultSlots, tabMode = "sidebar", tabs, tree, containerProps, children } = props;
23
23
  const isTop = useIsScrollTop({ enabled: navTransparentMode === "top" }) ?? true;
24
24
  const isNavTransparent = navTransparentMode === "top" ? isTop : navTransparentMode === "always";
25
- const { baseSlots, baseProps } = useProvider(props);
25
+ const { baseSlots, baseProps } = useBaseSlots(props);
26
26
  const linkItems = useLinkItems(props);
27
27
  const slots = {
28
28
  ...baseSlots,
@@ -33,10 +33,13 @@ function TOC({ container, header, footer, style = "normal", list }) {
33
33
  className: "inline-flex items-center gap-1.5 text-sm text-fd-muted-foreground",
34
34
  children: [/* @__PURE__ */ jsx(Text, { className: "size-4" }), t("On this page")]
35
35
  }),
36
- /* @__PURE__ */ jsx(TOCScrollArea, { children: /* @__PURE__ */ jsxs(TOCItems, {
37
- ...list,
38
- children: [items.length === 0 && /* @__PURE__ */ jsx(TOCEmpty, {}), items.map((item) => /* @__PURE__ */ jsx(TOCItem, { item }, item.url))]
39
- }) }),
36
+ /* @__PURE__ */ jsx(TOCScrollArea, {
37
+ className: "ms-px",
38
+ children: /* @__PURE__ */ jsxs(TOCItems, {
39
+ ...list,
40
+ children: [items.length === 0 && /* @__PURE__ */ jsx(TOCEmpty, {}), items.map((item) => /* @__PURE__ */ jsx(TOCItem, { item }, item.url))]
41
+ })
42
+ }),
40
43
  footer
41
44
  ]
42
45
  });
@@ -79,13 +82,16 @@ function TOCPopover({ container, trigger, content, header, footer, style = "norm
79
82
  ...content,
80
83
  children: [
81
84
  header,
82
- /* @__PURE__ */ jsx(TOCScrollArea, { children: /* @__PURE__ */ jsxs(TOCItems, {
83
- ...list,
84
- children: [items.length === 0 && /* @__PURE__ */ jsx(TOCEmpty, {}), items.map((item) => /* @__PURE__ */ jsx(TOCItem, {
85
- item,
86
- onClick: onClickItem
87
- }, item.url))]
88
- }) }),
85
+ /* @__PURE__ */ jsx(TOCScrollArea, {
86
+ className: "ms-px",
87
+ children: /* @__PURE__ */ jsxs(TOCItems, {
88
+ ...list,
89
+ children: [items.length === 0 && /* @__PURE__ */ jsx(TOCEmpty, {}), items.map((item) => /* @__PURE__ */ jsx(TOCItem, {
90
+ item,
91
+ onClick: onClickItem
92
+ }, item.url))]
93
+ })
94
+ }),
89
95
  footer
90
96
  ]
91
97
  })]
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../../../utils/cn.js";
3
3
  import { buttonVariants } from "../../../components/ui/button.js";
4
+ import { useTreePath } from "../../../contexts/tree.js";
4
5
  import { Popover, PopoverContent, PopoverTrigger } from "../../../components/ui/popover.js";
5
6
  import { LinkItem } from "../../shared/client.js";
6
7
  import { isLayoutTabActive } from "../../shared/index.js";
@@ -101,9 +102,14 @@ function Header(props) {
101
102
  }
102
103
  function LayoutHeaderTabs({ tabs, className, ...props }) {
103
104
  const pathname = usePathname();
105
+ const path = useTreePath();
104
106
  const selectedIdx = useMemo(() => {
105
- return tabs.findLastIndex((option) => isLayoutTabActive(option, pathname));
106
- }, [tabs, pathname]);
107
+ return tabs.findLastIndex((option) => isLayoutTabActive(option, path, pathname));
108
+ }, [
109
+ tabs,
110
+ path,
111
+ pathname
112
+ ]);
107
113
  return /* @__PURE__ */ jsx("div", {
108
114
  className: cn("flex flex-row items-end gap-6", className),
109
115
  ...props,
@@ -2,6 +2,7 @@
2
2
  import { cn } from "../../../utils/cn.js";
3
3
  import { buttonVariants } from "../../../components/ui/button.js";
4
4
  import { mergeRefs } from "../../../utils/merge-refs.js";
5
+ import { useTreePath } from "../../../contexts/tree.js";
5
6
  import { Popover, PopoverContent, PopoverTrigger } from "../../../components/ui/popover.js";
6
7
  import { LinkItem } from "../../shared/client.js";
7
8
  import { isLayoutTabActive } from "../../shared/index.js";
@@ -242,9 +243,14 @@ function SidebarTabsDropdown({ options, placeholder, ...props }) {
242
243
  const [open, setOpen] = useState(false);
243
244
  const { closeOnRedirect } = useSidebar();
244
245
  const pathname = usePathname();
246
+ const path = useTreePath();
245
247
  const selected = useMemo(() => {
246
- return options.findLast((item) => isLayoutTabActive(item, pathname));
247
- }, [options, pathname]);
248
+ return options.findLast((item) => isLayoutTabActive(item, path, pathname));
249
+ }, [
250
+ options,
251
+ path,
252
+ pathname
253
+ ]);
248
254
  const onClick = () => {
249
255
  closeOnRedirect.current = false;
250
256
  setOpen(false);