@fumadocs/ui 16.2.5

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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/css/base.css +217 -0
  3. package/css/colors/black.css +39 -0
  4. package/css/colors/catppuccin.css +49 -0
  5. package/css/colors/dusk.css +47 -0
  6. package/css/colors/index.css +51 -0
  7. package/css/colors/neutral.css +7 -0
  8. package/css/colors/ocean.css +48 -0
  9. package/css/colors/purple.css +39 -0
  10. package/css/colors/shadcn.css +36 -0
  11. package/css/colors/solar.css +75 -0
  12. package/css/colors/vitepress.css +77 -0
  13. package/css/shiki.css +90 -0
  14. package/dist/cn.d.ts +2 -0
  15. package/dist/cn.d.ts.map +1 -0
  16. package/dist/cn.js +1 -0
  17. package/dist/components/toc/clerk.d.ts +3 -0
  18. package/dist/components/toc/clerk.d.ts.map +1 -0
  19. package/dist/components/toc/clerk.js +76 -0
  20. package/dist/components/toc/default.d.ts +3 -0
  21. package/dist/components/toc/default.d.ts.map +1 -0
  22. package/dist/components/toc/default.js +19 -0
  23. package/dist/components/toc/index.d.ts +11 -0
  24. package/dist/components/toc/index.d.ts.map +1 -0
  25. package/dist/components/toc/index.js +64 -0
  26. package/dist/contexts/i18n.d.ts +50 -0
  27. package/dist/contexts/i18n.d.ts.map +1 -0
  28. package/dist/contexts/i18n.js +55 -0
  29. package/dist/contexts/search.d.ts +61 -0
  30. package/dist/contexts/search.d.ts.map +1 -0
  31. package/dist/contexts/search.js +59 -0
  32. package/dist/contexts/tree.d.ts +15 -0
  33. package/dist/contexts/tree.d.ts.map +1 -0
  34. package/dist/contexts/tree.js +32 -0
  35. package/dist/hooks/use-copy-button.d.ts +3 -0
  36. package/dist/hooks/use-copy-button.d.ts.map +1 -0
  37. package/dist/hooks/use-copy-button.js +27 -0
  38. package/dist/hooks/use-footer-items.d.ts +6 -0
  39. package/dist/hooks/use-footer-items.d.ts.map +1 -0
  40. package/dist/hooks/use-footer-items.js +27 -0
  41. package/dist/hooks/use-is-scroll-top.d.ts +4 -0
  42. package/dist/hooks/use-is-scroll-top.d.ts.map +1 -0
  43. package/dist/hooks/use-is-scroll-top.js +17 -0
  44. package/dist/i18n.d.ts +14 -0
  45. package/dist/i18n.d.ts.map +1 -0
  46. package/dist/i18n.js +16 -0
  47. package/dist/icons.d.ts +49 -0
  48. package/dist/icons.d.ts.map +1 -0
  49. package/dist/icons.js +281 -0
  50. package/dist/link-item.d.ts +78 -0
  51. package/dist/link-item.d.ts.map +1 -0
  52. package/dist/link-item.js +12 -0
  53. package/dist/merge-refs.d.ts +3 -0
  54. package/dist/merge-refs.d.ts.map +1 -0
  55. package/dist/merge-refs.js +12 -0
  56. package/dist/og/next.d.ts +15 -0
  57. package/dist/og/next.d.ts.map +1 -0
  58. package/dist/og/next.js +45 -0
  59. package/dist/typography/index.d.ts +10 -0
  60. package/dist/typography/index.d.ts.map +1 -0
  61. package/dist/typography/index.js +137 -0
  62. package/dist/typography/styles.d.ts +93 -0
  63. package/dist/typography/styles.d.ts.map +1 -0
  64. package/dist/typography/styles.js +433 -0
  65. package/dist/urls.d.ts +6 -0
  66. package/dist/urls.d.ts.map +1 -0
  67. package/dist/urls.js +13 -0
  68. package/package.json +75 -0
@@ -0,0 +1,77 @@
1
+ @import './index.css';
2
+
3
+ @theme {
4
+ --color-fd-muted: hsl(0, 0%, 96.1%);
5
+ --color-fd-popover: hsl(0, 0%, 100%);
6
+ --color-fd-popover-foreground: hsl(0, 0%, 15.1%);
7
+ --color-fd-card-foreground: hsl(0, 0%, 3.9%);
8
+ --color-fd-border: hsl(0, 0%, 89.8%);
9
+ --color-fd-primary-foreground: hsl(0, 0%, 98%);
10
+ --color-fd-secondary-foreground: hsl(0, 0%, 9%);
11
+ --color-fd-accent: hsl(0, 0%, 94.1%);
12
+ --color-fd-ring: hsl(0, 0%, 63.9%);
13
+
14
+ --color-fd-background: hsl(0, 0%, 100%);
15
+ --color-fd-card: hsl(0, 0%, 100%);
16
+ --color-fd-foreground: hsl(240, 6%, 25%);
17
+ --color-fd-muted-foreground: hsl(240, 6%, 50%);
18
+ --color-fd-secondary: hsl(240, 6%, 97%);
19
+ --color-fd-accent-foreground: hsl(240, 6%, 25%);
20
+ --color-fd-primary: hsl(226, 55%, 45%);
21
+ }
22
+
23
+ .dark {
24
+ --color-fd-ring: hsl(234, 100%, 83%);
25
+ --color-fd-primary: hsl(234, 100%, 83%);
26
+ --color-fd-secondary-foreground: hsl(60, 100%, 98%);
27
+ --color-fd-card-foreground: hsl(60, 100%, 98%);
28
+ --color-fd-background: hsl(240, 7%, 11%);
29
+ --color-fd-foreground: hsl(60, 100%, 98%);
30
+ --color-fd-popover: hsl(240, 7%, 11%);
31
+ --color-fd-popover-foreground: hsl(60, 100%, 98%);
32
+ --color-fd-primary-foreground: hsl(240, 7%, 11%);
33
+ --color-fd-card: hsl(240, 7%, 11%);
34
+ --color-fd-muted: hsl(0, 0%, 13%);
35
+ --color-fd-border: hsl(240, 4%, 19%);
36
+ --color-fd-accent: hsl(0, 0%, 15%);
37
+ --color-fd-secondary: hsl(240, 4%, 9%);
38
+ --color-fd-accent-foreground: hsl(0, 0%, 100%);
39
+ --color-fd-muted-foreground: hsl(240, 4%, 65%);
40
+ }
41
+
42
+ .prose {
43
+ --tw-prose-body: color-mix(
44
+ in oklab,
45
+ var(--color-fd-foreground) 85%,
46
+ transparent
47
+ );
48
+ --tw-prose-headings: color-mix(
49
+ in oklab,
50
+ var(--color-fd-foreground) 85%,
51
+ transparent
52
+ );
53
+ --tw-prose-links: var(--color-fd-primary);
54
+ --tw-prose-code: var(--color-fd-primary);
55
+ }
56
+
57
+ .prose :where(code):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
58
+ border: none;
59
+ background-color: color-mix(
60
+ in oklab,
61
+ var(--color-fd-primary) 10%,
62
+ transparent
63
+ );
64
+ }
65
+
66
+ #nd-sidebar {
67
+ background-color: hsl(240, 6%, 97%);
68
+ border-color: transparent;
69
+ }
70
+
71
+ .dark #nd-sidebar {
72
+ background-color: hsl(240, 4%, 9%);
73
+ }
74
+
75
+ button[data-search-full] {
76
+ background-color: var(--color-fd-background);
77
+ }
package/css/shiki.css ADDED
@@ -0,0 +1,90 @@
1
+ .shiki:not(.not-fumadocs-codeblock *) {
2
+ --padding-left: calc(var(--spacing) * 4);
3
+ --padding-right: calc(var(--spacing) * 4);
4
+
5
+ code span {
6
+ color: var(--shiki-light);
7
+ }
8
+ }
9
+
10
+ .dark .shiki:not(.not-fumadocs-codeblock *) {
11
+ code span {
12
+ color: var(--shiki-dark);
13
+ }
14
+ }
15
+
16
+ :is(pre *):is(.shiki *):not(.not-fumadocs-codeblock *) {
17
+ .line& {
18
+ position: relative;
19
+ min-height: 1lh;
20
+ padding-left: var(--padding-left);
21
+ padding-right: var(--padding-right);
22
+ }
23
+
24
+ .has-focused .line&:not(.focused) {
25
+ filter: blur(2px);
26
+ transition: filter 200ms;
27
+ }
28
+
29
+ .has-focused:hover .line&:not(.focused) {
30
+ filter: blur(0);
31
+ }
32
+
33
+ [data-line-numbers] .twoslash-meta-line& {
34
+ padding-left: calc(var(--padding-left) + 7 * var(--spacing));
35
+ }
36
+
37
+ [data-line-numbers] .line& {
38
+ counter-increment: line;
39
+ padding-left: calc(var(--padding-left) + 7 * var(--spacing));
40
+ }
41
+
42
+ [data-line-numbers] .line&::after {
43
+ position: absolute;
44
+ content: counter(line);
45
+ color: color-mix(
46
+ in oklab,
47
+ var(--fd-counter-color, var(--color-fd-muted-foreground)) 60%,
48
+ transparent
49
+ );
50
+ @apply top-0 left-4;
51
+ }
52
+
53
+ .diff&::before {
54
+ position: absolute;
55
+ left: calc(var(--spacing) * 1.5);
56
+ }
57
+
58
+ .diff.remove& {
59
+ opacity: 0.7;
60
+ --fd-counter-color: var(--color-fd-diff-remove-symbol);
61
+ @apply bg-fd-diff-remove;
62
+ }
63
+
64
+ .diff.remove&::before {
65
+ content: '-';
66
+ @apply text-fd-diff-remove-symbol;
67
+ }
68
+
69
+ .diff.add& {
70
+ --fd-counter-color: var(--color-fd-diff-add-symbol);
71
+ @apply bg-fd-diff-add;
72
+ }
73
+
74
+ .diff.add&::before {
75
+ content: '+';
76
+ @apply text-fd-diff-add-symbol;
77
+ }
78
+
79
+ .highlighted& {
80
+ --fd-counter-color: var(--color-fd-primary);
81
+ padding-left: calc(var(--padding-left) - 2px);
82
+
83
+ @apply border-l-2 border-fd-primary/50 bg-fd-primary/10;
84
+ }
85
+
86
+ .highlighted-word& {
87
+ padding: 1px;
88
+ @apply border -my-px border-fd-primary/30 bg-fd-primary/10 rounded-md font-medium;
89
+ }
90
+ }
package/dist/cn.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { twMerge as cn } from 'tailwind-merge';
2
+ //# sourceMappingURL=cn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cn.d.ts","sourceRoot":"","sources":["../src/cn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/cn.js ADDED
@@ -0,0 +1 @@
1
+ export { twMerge as cn } from 'tailwind-merge';
@@ -0,0 +1,3 @@
1
+ import { type ComponentProps } from 'react';
2
+ export declare function TOCItems({ ref, className, ...props }: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
3
+ //# sourceMappingURL=clerk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clerk.d.ts","sourceRoot":"","sources":["../../../src/components/toc/clerk.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAA+B,MAAM,OAAO,CAAC;AAMzE,wBAAgB,QAAQ,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,2CAsG3E"}
@@ -0,0 +1,76 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as Primitive from 'fumadocs-core/toc';
4
+ import { useEffect, useRef, useState } from 'react';
5
+ import { cn } from '../../cn.js';
6
+ import { TocThumb, useTOCItems } from './index.js';
7
+ import { mergeRefs } from '../../merge-refs.js';
8
+ import { useI18n } from '../../contexts/i18n.js';
9
+ export function TOCItems({ ref, className, ...props }) {
10
+ const containerRef = useRef(null);
11
+ const items = useTOCItems();
12
+ const { text } = useI18n();
13
+ const [svg, setSvg] = useState();
14
+ useEffect(() => {
15
+ if (!containerRef.current)
16
+ return;
17
+ const container = containerRef.current;
18
+ function onResize() {
19
+ if (container.clientHeight === 0)
20
+ return;
21
+ let w = 0, h = 0;
22
+ const d = [];
23
+ for (let i = 0; i < items.length; i++) {
24
+ const element = container.querySelector(`a[href="#${items[i].url.slice(1)}"]`);
25
+ if (!element)
26
+ continue;
27
+ const styles = getComputedStyle(element);
28
+ const offset = getLineOffset(items[i].depth) + 1, top = element.offsetTop + parseFloat(styles.paddingTop), bottom = element.offsetTop +
29
+ element.clientHeight -
30
+ parseFloat(styles.paddingBottom);
31
+ w = Math.max(offset, w);
32
+ h = Math.max(h, bottom);
33
+ d.push(`${i === 0 ? 'M' : 'L'}${offset} ${top}`);
34
+ d.push(`L${offset} ${bottom}`);
35
+ }
36
+ setSvg({
37
+ path: d.join(' '),
38
+ width: w + 1,
39
+ height: h,
40
+ });
41
+ }
42
+ const observer = new ResizeObserver(onResize);
43
+ onResize();
44
+ observer.observe(container);
45
+ return () => {
46
+ observer.disconnect();
47
+ };
48
+ }, [items]);
49
+ if (items.length === 0)
50
+ return (_jsx("div", { className: "rounded-lg border bg-fd-card p-3 text-xs text-fd-muted-foreground", children: text.tocNoHeadings }));
51
+ return (_jsxs(_Fragment, { children: [svg && (_jsx("div", { className: "absolute start-0 top-0 rtl:-scale-x-100", style: {
52
+ width: svg.width,
53
+ height: svg.height,
54
+ maskImage: `url("data:image/svg+xml,${
55
+ // Inline SVG
56
+ encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${svg.width} ${svg.height}"><path d="${svg.path}" stroke="black" stroke-width="1" fill="none" /></svg>`)}")`,
57
+ }, children: _jsx(TocThumb, { containerRef: containerRef, className: "translate-y-(--fd-top) h-(--fd-height) bg-fd-primary transition-[translate,height]" }) })), _jsx("div", { ref: mergeRefs(containerRef, ref), className: cn('flex flex-col', className), ...props, children: items.map((item, i) => (_jsx(TOCItem, { item: item, upper: items[i - 1]?.depth, lower: items[i + 1]?.depth }, item.url))) })] }));
58
+ }
59
+ function getItemOffset(depth) {
60
+ if (depth <= 2)
61
+ return 14;
62
+ if (depth === 3)
63
+ return 26;
64
+ return 36;
65
+ }
66
+ function getLineOffset(depth) {
67
+ return depth >= 3 ? 10 : 0;
68
+ }
69
+ function TOCItem({ item, upper = item.depth, lower = item.depth, }) {
70
+ const offset = getLineOffset(item.depth), upperOffset = getLineOffset(upper), lowerOffset = getLineOffset(lower);
71
+ return (_jsxs(Primitive.TOCItem, { href: item.url, style: {
72
+ paddingInlineStart: getItemOffset(item.depth),
73
+ }, className: "prose relative py-1.5 text-sm text-fd-muted-foreground hover:text-fd-accent-foreground transition-colors wrap-anywhere first:pt-0 last:pb-0 data-[active=true]:text-fd-primary", children: [offset !== upperOffset && (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", className: "absolute -top-1.5 start-0 size-4 rtl:-scale-x-100", children: _jsx("line", { x1: upperOffset, y1: "0", x2: offset, y2: "12", className: "stroke-fd-foreground/10", strokeWidth: "1" }) })), _jsx("div", { className: cn('absolute inset-y-0 w-px bg-fd-foreground/10', offset !== upperOffset && 'top-1.5', offset !== lowerOffset && 'bottom-1.5'), style: {
74
+ insetInlineStart: offset,
75
+ } }), item.title] }));
76
+ }
@@ -0,0 +1,3 @@
1
+ import { type ComponentProps } from 'react';
2
+ export declare function TOCItems({ ref, className, ...props }: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
3
+ //# sourceMappingURL=default.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../src/components/toc/default.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,cAAc,EAAU,MAAM,OAAO,CAAC;AAKpD,wBAAgB,QAAQ,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,KAAK,CAAC,2CAgC3E"}
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useI18n } from '../../contexts/i18n.js';
4
+ import { cn } from '../../cn.js';
5
+ import { useRef } from 'react';
6
+ import { mergeRefs } from '../../merge-refs.js';
7
+ import { TocThumb, useTOCItems } from './index.js';
8
+ import * as Primitive from 'fumadocs-core/toc';
9
+ export function TOCItems({ ref, className, ...props }) {
10
+ const containerRef = useRef(null);
11
+ const items = useTOCItems();
12
+ const { text } = useI18n();
13
+ if (items.length === 0)
14
+ return (_jsx("div", { className: "rounded-lg border bg-fd-card p-3 text-xs text-fd-muted-foreground", children: text.tocNoHeadings }));
15
+ return (_jsxs(_Fragment, { children: [_jsx(TocThumb, { containerRef: containerRef, className: "absolute top-0 translate-y-(--fd-top) h-(--fd-height) w-px bg-fd-primary transition-[translate,height]" }), _jsx("div", { ref: mergeRefs(ref, containerRef), className: cn('flex flex-col border-s border-fd-foreground/10', className), ...props, children: items.map((item) => (_jsx(TOCItem, { item: item }, item.url))) })] }));
16
+ }
17
+ function TOCItem({ item }) {
18
+ return (_jsx(Primitive.TOCItem, { href: item.url, className: cn('prose py-1.5 text-sm text-fd-muted-foreground transition-colors wrap-anywhere first:pt-0 last:pb-0 data-[active=true]:text-fd-primary', item.depth <= 2 && 'ps-3', item.depth === 3 && 'ps-6', item.depth >= 4 && 'ps-8'), children: item.title }));
19
+ }
@@ -0,0 +1,11 @@
1
+ import * as Primitive from 'fumadocs-core/toc';
2
+ import { type ComponentProps, type RefObject } from 'react';
3
+ export declare function useTOCItems(): Primitive.TOCItemType[];
4
+ export declare function TOCProvider({ toc, children, ...props }: ComponentProps<typeof Primitive.AnchorProvider>): import("react/jsx-runtime").JSX.Element;
5
+ export declare function TOCScrollArea({ ref, className, ...props }: ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
6
+ interface RefProps {
7
+ containerRef: RefObject<HTMLElement | null>;
8
+ }
9
+ export declare function TocThumb({ containerRef, ...props }: ComponentProps<'div'> & RefProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/toc/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AAOf,wBAAgB,WAAW,IAAI,SAAS,CAAC,WAAW,EAAE,CAErD;AAED,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,2CAQjD;AAED,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,SAAS,EACT,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,KAAK,CAAC,2CAiBvB;AAID,UAAU,QAAQ;IAChB,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC7C;AAED,wBAAgB,QAAQ,CAAC,EACvB,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,QAAQ,2CA4BlC"}
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as Primitive from 'fumadocs-core/toc';
4
+ import { createContext, use, useEffect, useEffectEvent, useRef, } from 'react';
5
+ import { cn } from '../../cn.js';
6
+ import { mergeRefs } from '../../merge-refs.js';
7
+ import { useOnChange } from 'fumadocs-core/utils/use-on-change';
8
+ const TOCContext = createContext([]);
9
+ export function useTOCItems() {
10
+ return use(TOCContext);
11
+ }
12
+ export function TOCProvider({ toc, children, ...props }) {
13
+ return (_jsx(TOCContext, { value: toc, children: _jsx(Primitive.AnchorProvider, { toc: toc, ...props, children: children }) }));
14
+ }
15
+ export function TOCScrollArea({ ref, className, ...props }) {
16
+ const viewRef = useRef(null);
17
+ return (_jsx("div", { ref: mergeRefs(viewRef, ref), className: cn('relative min-h-0 text-sm ms-px overflow-auto [scrollbar-width:none] mask-[linear-gradient(to_bottom,transparent,white_16px,white_calc(100%-16px),transparent)] py-3', className), ...props, children: _jsx(Primitive.ScrollProvider, { containerRef: viewRef, children: props.children }) }));
18
+ }
19
+ export function TocThumb({ containerRef, ...props }) {
20
+ const thumbRef = useRef(null);
21
+ const active = Primitive.useActiveAnchors();
22
+ const onPrint = useEffectEvent(() => {
23
+ if (!containerRef.current || !thumbRef.current)
24
+ return;
25
+ update(thumbRef.current, calc(containerRef.current, active));
26
+ });
27
+ useEffect(() => {
28
+ if (!containerRef.current)
29
+ return;
30
+ const container = containerRef.current;
31
+ const observer = new ResizeObserver(onPrint);
32
+ observer.observe(container);
33
+ return () => {
34
+ observer.disconnect();
35
+ };
36
+ }, [containerRef]);
37
+ useOnChange(active, () => {
38
+ if (containerRef.current && thumbRef.current) {
39
+ update(thumbRef.current, calc(containerRef.current, active));
40
+ }
41
+ });
42
+ return _jsx("div", { ref: thumbRef, role: "none", ...props });
43
+ }
44
+ function calc(container, active) {
45
+ if (active.length === 0 || container.clientHeight === 0) {
46
+ return [0, 0];
47
+ }
48
+ let upper = Number.MAX_VALUE, lower = 0;
49
+ for (const item of active) {
50
+ const element = container.querySelector(`a[href="#${item}"]`);
51
+ if (!element)
52
+ continue;
53
+ const styles = getComputedStyle(element);
54
+ upper = Math.min(upper, element.offsetTop + parseFloat(styles.paddingTop));
55
+ lower = Math.max(lower, element.offsetTop +
56
+ element.clientHeight -
57
+ parseFloat(styles.paddingBottom));
58
+ }
59
+ return [upper, lower - upper];
60
+ }
61
+ function update(element, info) {
62
+ element.style.setProperty('--fd-top', `${info[0]}px`);
63
+ element.style.setProperty('--fd-height', `${info[1]}px`);
64
+ }
@@ -0,0 +1,50 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface Translations {
3
+ search: string;
4
+ searchNoResult: string;
5
+ toc: string;
6
+ tocNoHeadings: string;
7
+ lastUpdate: string;
8
+ chooseLanguage: string;
9
+ nextPage: string;
10
+ previousPage: string;
11
+ chooseTheme: string;
12
+ editOnGithub: string;
13
+ }
14
+ export interface LocaleItem {
15
+ name: string;
16
+ locale: string;
17
+ }
18
+ interface I18nContextType {
19
+ locale?: string;
20
+ onChange?: (v: string) => void;
21
+ text: Translations;
22
+ locales?: LocaleItem[];
23
+ }
24
+ export declare const defaultTranslations: Translations;
25
+ export declare function I18nLabel(props: {
26
+ label: keyof Translations;
27
+ }): string;
28
+ export declare function useI18n(): I18nContextType;
29
+ export interface I18nProviderProps {
30
+ /**
31
+ * Current locale
32
+ */
33
+ locale: string;
34
+ /**
35
+ * Handle changes to the locale, redirect user when not specified.
36
+ */
37
+ onLocaleChange?: (v: string) => void;
38
+ /**
39
+ * Translations of current locale
40
+ */
41
+ translations?: Partial<Translations>;
42
+ /**
43
+ * Available languages
44
+ */
45
+ locales?: LocaleItem[];
46
+ children?: ReactNode;
47
+ }
48
+ export declare function I18nProvider({ locales, locale, onLocaleChange, children, translations, }: I18nProviderProps): import("react/jsx-runtime").JSX.Element;
49
+ export {};
50
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/contexts/i18n.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,SAAS,EAIf,MAAM,OAAO,CAAC;AAGf,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IAEvB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IAEtB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,EAAE,YAWjC,CAAC;AAMF,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,YAAY,CAAA;CAAE,GAAG,MAAM,CAItE;AAED,wBAAgB,OAAO,IAAI,eAAe,CAEzC;AAED,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAErC;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAErC;;OAEG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IAEvB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,OAAY,EACZ,MAAM,EACN,cAAc,EACd,QAAQ,EACR,YAAY,GACb,EAAE,iBAAiB,2CAuCnB"}
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext, useMemo, useRef, } from 'react';
4
+ import { usePathname, useRouter } from 'fumadocs-core/framework';
5
+ export const defaultTranslations = {
6
+ search: 'Search',
7
+ searchNoResult: 'No results found',
8
+ toc: 'On this page',
9
+ tocNoHeadings: 'No Headings',
10
+ lastUpdate: 'Last updated on',
11
+ chooseLanguage: 'Choose a language',
12
+ nextPage: 'Next Page',
13
+ previousPage: 'Previous Page',
14
+ chooseTheme: 'Theme',
15
+ editOnGithub: 'Edit on GitHub',
16
+ };
17
+ const I18nContext = createContext({
18
+ text: defaultTranslations,
19
+ });
20
+ export function I18nLabel(props) {
21
+ const { text } = useI18n();
22
+ return text[props.label];
23
+ }
24
+ export function useI18n() {
25
+ return useContext(I18nContext);
26
+ }
27
+ export function I18nProvider({ locales = [], locale, onLocaleChange, children, translations, }) {
28
+ const router = useRouter();
29
+ const pathname = usePathname();
30
+ const onChange = (value) => {
31
+ if (onLocaleChange) {
32
+ return onLocaleChange(value);
33
+ }
34
+ const segments = pathname.split('/').filter((v) => v.length > 0);
35
+ // If locale prefix hidden
36
+ if (segments[0] !== locale) {
37
+ segments.unshift(value);
38
+ }
39
+ else {
40
+ segments[0] = value;
41
+ }
42
+ router.push(`/${segments.join('/')}`);
43
+ };
44
+ const onChangeRef = useRef(onChange);
45
+ onChangeRef.current = onChange;
46
+ return (_jsx(I18nContext, { value: useMemo(() => ({
47
+ locale,
48
+ locales,
49
+ text: {
50
+ ...defaultTranslations,
51
+ ...translations,
52
+ },
53
+ onChange: (v) => onChangeRef.current(v),
54
+ }), [locale, locales, translations]), children: children }));
55
+ }
@@ -0,0 +1,61 @@
1
+ import { type ComponentType, type ReactNode } from 'react';
2
+ interface HotKey {
3
+ display: ReactNode;
4
+ /**
5
+ * Key code or a function determining whether the key is pressed.
6
+ */
7
+ key: string | ((e: KeyboardEvent) => boolean);
8
+ }
9
+ export interface SharedProps {
10
+ open: boolean;
11
+ onOpenChange: (open: boolean) => void;
12
+ }
13
+ export type SearchLink = [name: string, href: string];
14
+ export interface TagItem {
15
+ name: string;
16
+ value: string;
17
+ }
18
+ export interface SearchProviderProps {
19
+ /**
20
+ * Preload search dialog before opening it
21
+ *
22
+ * @defaultValue `true`
23
+ */
24
+ preload?: boolean;
25
+ /**
26
+ * Custom links to be displayed if search is empty
27
+ */
28
+ links?: SearchLink[];
29
+ /**
30
+ * Hotkeys for triggering search dialog
31
+ *
32
+ * @defaultValue Meta/Ctrl + K
33
+ */
34
+ hotKey?: HotKey[];
35
+ /**
36
+ * Replace default search dialog, allowing you to use other solutions such as Algolia Search
37
+ *
38
+ * It receives the `open` and `onOpenChange` prop, can be lazy loaded with `next/dynamic`
39
+ */
40
+ SearchDialog: ComponentType<SharedProps>;
41
+ /**
42
+ * Additional props to the dialog
43
+ */
44
+ options?: Partial<SharedProps & Record<string, unknown>>;
45
+ children?: ReactNode;
46
+ }
47
+ interface SearchContextType {
48
+ enabled: boolean;
49
+ hotKey: HotKey[];
50
+ setOpenSearch: (value: boolean) => void;
51
+ }
52
+ export declare function useSearchContext(): SearchContextType;
53
+ export declare function SearchProvider({ SearchDialog, children, preload, options, hotKey, links, }: SearchProviderProps): import("react/jsx-runtime").JSX.Element;
54
+ /**
55
+ * Show children only when search is enabled via React Context
56
+ */
57
+ export declare function SearchOnly({ children }: {
58
+ children: ReactNode;
59
+ }): ReactNode;
60
+ export {};
61
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/contexts/search.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAEf,UAAU,MAAM;IACd,OAAO,EAAE,SAAS,CAAC;IAEnB;;OAEG;IACH,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAEtD,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IAErB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAElB;;;;OAIG;IACH,YAAY,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAEzC;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEzD,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AAQD,wBAAgB,gBAAgB,IAAI,iBAAiB,CAEpD;AAcD,wBAAgB,cAAc,CAAC,EAC7B,YAAY,EACZ,QAAQ,EACR,OAAc,EACd,OAAO,EACP,MASC,EACD,KAAK,GACN,EAAE,mBAAmB,2CA2CrB;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,aAI/D"}
@@ -0,0 +1,59 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { createContext, use, useEffect, useEffectEvent, useMemo, useState, } from 'react';
4
+ const SearchContext = createContext({
5
+ enabled: false,
6
+ hotKey: [],
7
+ setOpenSearch: () => undefined,
8
+ });
9
+ export function useSearchContext() {
10
+ return use(SearchContext);
11
+ }
12
+ function MetaOrControl() {
13
+ const [key, setKey] = useState('⌘');
14
+ useEffect(() => {
15
+ const isWindows = window.navigator.userAgent.includes('Windows');
16
+ if (isWindows)
17
+ setKey('Ctrl');
18
+ }, []);
19
+ return key;
20
+ }
21
+ export function SearchProvider({ SearchDialog, children, preload = true, options, hotKey = [
22
+ {
23
+ key: (e) => e.metaKey || e.ctrlKey,
24
+ display: _jsx(MetaOrControl, {}),
25
+ },
26
+ {
27
+ key: 'k',
28
+ display: 'K',
29
+ },
30
+ ], links, }) {
31
+ const [isOpen, setIsOpen] = useState(preload ? false : undefined);
32
+ const onKeyDown = useEffectEvent((e) => {
33
+ if (hotKey.every((v) => typeof v.key === 'string' ? e.key === v.key : v.key(e))) {
34
+ setIsOpen((open) => !open);
35
+ e.preventDefault();
36
+ }
37
+ });
38
+ useEffect(() => {
39
+ window.addEventListener('keydown', onKeyDown);
40
+ return () => {
41
+ window.removeEventListener('keydown', onKeyDown);
42
+ };
43
+ }, [hotKey]);
44
+ return (_jsxs(SearchContext, { value: useMemo(() => ({
45
+ enabled: true,
46
+ hotKey,
47
+ setOpenSearch: setIsOpen,
48
+ }), [hotKey]), children: [isOpen !== undefined && (_jsx(SearchDialog, { open: isOpen, onOpenChange: setIsOpen,
49
+ // @ts-expect-error -- insert prop for official UIs
50
+ links: links, ...options })), children] }));
51
+ }
52
+ /**
53
+ * Show children only when search is enabled via React Context
54
+ */
55
+ export function SearchOnly({ children }) {
56
+ const search = useSearchContext();
57
+ if (search.enabled)
58
+ return children;
59
+ }
@@ -0,0 +1,15 @@
1
+ import type * as PageTree from 'fumadocs-core/page-tree';
2
+ import { type ReactNode } from 'react';
3
+ type MakeRequired<O, K extends keyof O> = Omit<O, K> & Pick<Required<O>, K>;
4
+ interface TreeContextType {
5
+ root: MakeRequired<PageTree.Root | PageTree.Folder, '$id'>;
6
+ full: PageTree.Root;
7
+ }
8
+ export declare function TreeContextProvider({ tree: rawTree, children, }: {
9
+ tree: PageTree.Root;
10
+ children: ReactNode;
11
+ }): import("react/jsx-runtime").JSX.Element;
12
+ export declare function useTreePath(): PageTree.Node[];
13
+ export declare function useTreeContext(): TreeContextType;
14
+ export {};
15
+ //# sourceMappingURL=tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/contexts/tree.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,KAAK,SAAS,EAAuC,MAAM,OAAO,CAAC;AAG5E,KAAK,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE5E,UAAU,eAAe;IACvB,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3D,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;CACrB;AAKD,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EAAE,OAAO,EACb,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB,2CA8BA;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAAC,IAAI,EAAE,CAE7C;AAED,wBAAgB,cAAc,IAAI,eAAe,CAMhD"}
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { usePathname } from 'fumadocs-core/framework';
4
+ import { useMemo, useRef, createContext, use } from 'react';
5
+ import { searchPath } from 'fumadocs-core/breadcrumb';
6
+ const TreeContext = createContext(null);
7
+ const PathContext = createContext([]);
8
+ export function TreeContextProvider({ tree: rawTree, children, }) {
9
+ const nextIdRef = useRef(0);
10
+ const pathname = usePathname();
11
+ // I found that object-typed props passed from a RSC will be re-constructed, hence breaking all hooks' dependencies
12
+ // using the id here to make sure this never happens
13
+ // eslint-disable-next-line react-hooks/exhaustive-deps
14
+ const tree = useMemo(() => rawTree, [rawTree.$id ?? rawTree]);
15
+ const path = useMemo(() => {
16
+ return (searchPath(tree.children, pathname) ??
17
+ (tree.fallback ? searchPath(tree.fallback.children, pathname) : null) ??
18
+ []);
19
+ }, [tree, pathname]);
20
+ const root = path.findLast((item) => item.type === 'folder' && item.root) ?? tree;
21
+ root.$id ?? (root.$id = String(nextIdRef.current++));
22
+ return (_jsx(TreeContext, { value: useMemo(() => ({ root, full: tree }), [root, tree]), children: _jsx(PathContext, { value: path, children: children }) }));
23
+ }
24
+ export function useTreePath() {
25
+ return use(PathContext);
26
+ }
27
+ export function useTreeContext() {
28
+ const ctx = use(TreeContext);
29
+ if (!ctx)
30
+ throw new Error('You must wrap this component under <DocsLayout />');
31
+ return ctx;
32
+ }