mirror of
https://github.com/prdlk/leetcode.git
synced 2026-08-02 17:31:40 +00:00
docs(docs): add cli configuration and components
This commit is contained in:
@@ -9,10 +9,15 @@
|
|||||||
"@fontsource-variable/dm-sans": "latest",
|
"@fontsource-variable/dm-sans": "latest",
|
||||||
"@fontsource-variable/instrument-sans": "latest",
|
"@fontsource-variable/instrument-sans": "latest",
|
||||||
"@fontsource-variable/jetbrains-mono": "latest",
|
"@fontsource-variable/jetbrains-mono": "latest",
|
||||||
|
"@fuma-translate/react": "^1.0.2",
|
||||||
|
"@fumadocs/base-ui": "^16.11.3",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"cnfast": "^0.0.8",
|
||||||
"fumadocs-core": "latest",
|
"fumadocs-core": "latest",
|
||||||
"fumadocs-mdx": "latest",
|
"fumadocs-mdx": "latest",
|
||||||
"fumadocs-ui": "npm:@fumadocs/base-ui@latest",
|
"fumadocs-ui": "npm:@fumadocs/base-ui@latest",
|
||||||
"fumapress": "latest",
|
"fumapress": "latest",
|
||||||
|
"lucide-react": "^1.23.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-server-dom-webpack": "^19.2.0",
|
"react-server-dom-webpack": "^19.2.0",
|
||||||
@@ -123,6 +128,8 @@
|
|||||||
|
|
||||||
"@fuma-translate/react": ["@fuma-translate/react@1.0.2", "", { "peerDependencies": { "@types/react": "*", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalPeers": ["@types/react"] }, "sha512-uOiOtBx3nRXR8Nu1GzBf1tApgF1FErDBTHxRIAQeyQdyOoZbrNRN6H4kDCWObY4qyGeGbHydG0DHzgeUgFDMIw=="],
|
"@fuma-translate/react": ["@fuma-translate/react@1.0.2", "", { "peerDependencies": { "@types/react": "*", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalPeers": ["@types/react"] }, "sha512-uOiOtBx3nRXR8Nu1GzBf1tApgF1FErDBTHxRIAQeyQdyOoZbrNRN6H4kDCWObY4qyGeGbHydG0DHzgeUgFDMIw=="],
|
||||||
|
|
||||||
|
"@fumadocs/base-ui": ["@fumadocs/base-ui@16.11.3", "", { "dependencies": { "@base-ui/react": "^1.6.0", "@fuma-translate/react": "^1.0.2", "@fumadocs/tailwind": "0.1.1", "class-variance-authority": "^0.7.1", "cnfast": "^0.0.8", "lucide-react": "^1.23.0", "motion": "^12.42.2", "next-themes": "^0.4.6", "react-remove-scroll": "^2.7.2", "rehype-raw": "^7.0.0", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.3.1", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "16.11.3", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "takumi-js": "*" }, "optionalPeers": ["@types/mdx", "@types/react", "next", "takumi-js"] }, "sha512-O3xELlkEXAEEbkfR1EzD2XIvrxxe5sJSNpCrSF2+uuJwX7qObDcvtGPsDNJzwOsynOqJYPsOvrG1kLkAILeasw=="],
|
||||||
|
|
||||||
"@fumadocs/tailwind": ["@fumadocs/tailwind@0.1.1", "", { "peerDependencies": { "tailwindcss": "^4.0.0" }, "optionalPeers": ["tailwindcss"] }, "sha512-BnPe52UxSaG8yKlHMKBxXw8h6GpK5qO55ci6+Qd5JnquTvIw6SpfbC1P+qAi82PuPWv1KZAWY8bxRk4+x9ctXw=="],
|
"@fumadocs/tailwind": ["@fumadocs/tailwind@0.1.1", "", { "peerDependencies": { "tailwindcss": "^4.0.0" }, "optionalPeers": ["tailwindcss"] }, "sha512-BnPe52UxSaG8yKlHMKBxXw8h6GpK5qO55ci6+Qd5JnquTvIw6SpfbC1P+qAi82PuPWv1KZAWY8bxRk4+x9ctXw=="],
|
||||||
|
|
||||||
"@hono/node-server": ["@hono/node-server@2.0.8", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GuCWzLxwg218fy1JaHculFsdcuY12hxit83V+algozTPnwhNjLrRL/Alg9OYjLZLoUZ1rw/S4CdTMsnkSKCmFA=="],
|
"@hono/node-server": ["@hono/node-server@2.0.8", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GuCWzLxwg218fy1JaHculFsdcuY12hxit83V+algozTPnwhNjLrRL/Alg9OYjLZLoUZ1rw/S4CdTMsnkSKCmFA=="],
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "node_modules/@fumadocs/cli/dist/schema.json",
|
||||||
|
"aliases": {
|
||||||
|
"uiDir": "./components/ui",
|
||||||
|
"componentsDir": "./components",
|
||||||
|
"layoutDir": "./layouts",
|
||||||
|
"cssDir": "./styles",
|
||||||
|
"libDir": "./lib"
|
||||||
|
},
|
||||||
|
"baseDir": "src",
|
||||||
|
"uiLibrary": "base-ui",
|
||||||
|
"framework": "waku",
|
||||||
|
"commands": {}
|
||||||
|
}
|
||||||
@@ -13,10 +13,15 @@
|
|||||||
"@fontsource-variable/dm-sans": "latest",
|
"@fontsource-variable/dm-sans": "latest",
|
||||||
"@fontsource-variable/instrument-sans": "latest",
|
"@fontsource-variable/instrument-sans": "latest",
|
||||||
"@fontsource-variable/jetbrains-mono": "latest",
|
"@fontsource-variable/jetbrains-mono": "latest",
|
||||||
|
"@fuma-translate/react": "^1.0.2",
|
||||||
|
"@fumadocs/base-ui": "^16.11.3",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"cnfast": "^0.0.8",
|
||||||
"fumadocs-core": "latest",
|
"fumadocs-core": "latest",
|
||||||
"fumadocs-mdx": "latest",
|
"fumadocs-mdx": "latest",
|
||||||
"fumadocs-ui": "npm:@fumadocs/base-ui@latest",
|
"fumadocs-ui": "npm:@fumadocs/base-ui@latest",
|
||||||
"fumapress": "latest",
|
"fumapress": "latest",
|
||||||
|
"lucide-react": "^1.23.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-server-dom-webpack": "^19.2.0",
|
"react-server-dom-webpack": "^19.2.0",
|
||||||
|
|||||||
+26
-4
@@ -1,5 +1,10 @@
|
|||||||
import { defineConfig } from "fumapress";
|
import { defineConfig } from "fumapress";
|
||||||
import { fumadocsMdx } from "fumapress/adapters/mdx";
|
import { fumadocsMdx } from "fumapress/adapters/mdx";
|
||||||
|
import defaultMdxComponents, { createRelativeLink } from "fumadocs-ui/mdx";
|
||||||
|
import { TypeTable } from "fumadocs-ui/components/type-table";
|
||||||
|
import { Step, Steps } from "fumadocs-ui/components/steps";
|
||||||
|
import { Tabs, Tab } from "fumadocs-ui/components/tabs";
|
||||||
|
import { Accordion, Accordions } from "fumadocs-ui/components/accordion";
|
||||||
import { flexsearchPlugin } from "fumapress/plugins/flexsearch";
|
import { flexsearchPlugin } from "fumapress/plugins/flexsearch";
|
||||||
import { llmsPlugin } from "fumapress/plugins/llms.txt";
|
import { llmsPlugin } from "fumapress/plugins/llms.txt";
|
||||||
import { docs } from "./.source/server";
|
import { docs } from "./.source/server";
|
||||||
@@ -9,11 +14,11 @@ export default defineConfig({
|
|||||||
name: "Docs",
|
name: "Docs",
|
||||||
baseUrl: import.meta.env.DEV
|
baseUrl: import.meta.env.DEV
|
||||||
? "http://localhost:3000"
|
? "http://localhost:3000"
|
||||||
: "https://docs.example.com",
|
: "https://cs.prad.nu",
|
||||||
git: {
|
git: {
|
||||||
user: "your-github-user",
|
user: "prdlk",
|
||||||
branch: "main",
|
branch: "main",
|
||||||
repo: "docs",
|
repo: "leetcode",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
@@ -26,4 +31,21 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.plugins(flexsearchPlugin(), llmsPlugin())
|
.plugins(flexsearchPlugin(), llmsPlugin())
|
||||||
.adapters(fumadocsMdx());
|
.adapters(
|
||||||
|
fumadocsMdx({
|
||||||
|
async getMdxComponents(page) {
|
||||||
|
const source = await this.getLoader();
|
||||||
|
return {
|
||||||
|
...defaultMdxComponents,
|
||||||
|
TypeTable,
|
||||||
|
Steps,
|
||||||
|
Step,
|
||||||
|
Tabs,
|
||||||
|
Tab,
|
||||||
|
Accordion,
|
||||||
|
Accordions,
|
||||||
|
a: createRelativeLink(source, page),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Check, LinkIcon } from 'lucide-react';
|
||||||
|
import { type ComponentProps, type ReactNode, useEffect, useRef, useState } from 'react';
|
||||||
|
import { cn } from '../lib/cn';
|
||||||
|
import { useCopyButton } from '@fumadocs/base-ui/utils/use-copy-button';
|
||||||
|
import { buttonVariants } from './ui/button';
|
||||||
|
import { mergeRefs } from '../lib/merge-refs';
|
||||||
|
import { useTranslations } from '@fuma-translate/react';
|
||||||
|
import {
|
||||||
|
Accordion as Root,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionHeader,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from './ui/accordion';
|
||||||
|
|
||||||
|
export function Accordions({
|
||||||
|
ref,
|
||||||
|
className,
|
||||||
|
defaultValue,
|
||||||
|
...props
|
||||||
|
}: ComponentProps<typeof Root>) {
|
||||||
|
const rootRef = useRef<HTMLDivElement>(null);
|
||||||
|
const composedRef = mergeRefs(ref, rootRef);
|
||||||
|
const [value, setValue] = useState<unknown[]>(defaultValue ?? []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = window.location.hash.substring(1);
|
||||||
|
const element = rootRef.current;
|
||||||
|
if (!element || id.length === 0) return;
|
||||||
|
|
||||||
|
const selected = document.getElementById(id);
|
||||||
|
if (!selected || !element.contains(selected)) return;
|
||||||
|
const value = selected.getAttribute('data-accordion-value');
|
||||||
|
|
||||||
|
if (value) setValue((prev) => [value, ...prev]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Root
|
||||||
|
ref={composedRef}
|
||||||
|
value={value}
|
||||||
|
onValueChange={setValue}
|
||||||
|
className={(s) =>
|
||||||
|
cn(
|
||||||
|
'divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card',
|
||||||
|
typeof className === 'function' ? className(s) : className,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Accordion({
|
||||||
|
title,
|
||||||
|
id,
|
||||||
|
value = String(title),
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: Omit<ComponentProps<typeof AccordionItem>, 'value' | 'title'> & {
|
||||||
|
title: string | ReactNode;
|
||||||
|
value?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<AccordionItem value={value} {...props}>
|
||||||
|
<AccordionHeader id={id} data-accordion-value={value}>
|
||||||
|
<AccordionTrigger>{title}</AccordionTrigger>
|
||||||
|
{id ? <CopyButton id={id} /> : null}
|
||||||
|
</AccordionHeader>
|
||||||
|
<AccordionContent hiddenUntilFound>
|
||||||
|
<div className="px-4 pb-2 text-[0.9375rem] prose-no-margin [&[hidden]:not([hidden='until-found'])]:hidden">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CopyButton({ id }: { id: string }) {
|
||||||
|
const t = useTranslations({ note: 'accordion' });
|
||||||
|
const [checked, onClick] = useCopyButton(() => {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.hash = id;
|
||||||
|
|
||||||
|
return navigator.clipboard.writeText(url.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label={t('Copy Link', { note: 'aria-label' })}
|
||||||
|
className={cn(
|
||||||
|
buttonVariants({
|
||||||
|
color: 'ghost',
|
||||||
|
className: 'text-fd-muted-foreground me-2',
|
||||||
|
}),
|
||||||
|
)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{checked ? <Check className="size-3.5" /> : <LinkIcon className="size-3.5" />}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export function Steps({ children }: { children: ReactNode }) {
|
||||||
|
return <div className="fd-steps">{children}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Step({ children }: { children: ReactNode }) {
|
||||||
|
return <div className="fd-step">{children}</div>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Accordion as Primitive } from '@base-ui/react/accordion';
|
||||||
|
import { ChevronRight } from 'lucide-react';
|
||||||
|
import { type ComponentProps } from 'react';
|
||||||
|
import { cn } from '../../lib/cn';
|
||||||
|
|
||||||
|
export function Accordion({ className, ...props }: ComponentProps<typeof Primitive.Root>) {
|
||||||
|
return (
|
||||||
|
<Primitive.Root
|
||||||
|
className={(s) =>
|
||||||
|
cn(
|
||||||
|
'divide-y divide-fd-border overflow-hidden rounded-lg border bg-fd-card',
|
||||||
|
typeof className === 'function' ? className(s) : className,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccordionItem({ children, ...props }: ComponentProps<typeof Primitive.Item>) {
|
||||||
|
return <Primitive.Item {...props}>{children}</Primitive.Item>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccordionHeader({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: ComponentProps<typeof Primitive.Header>) {
|
||||||
|
return (
|
||||||
|
<Primitive.Header
|
||||||
|
className={(s) =>
|
||||||
|
cn(
|
||||||
|
'scroll-m-24 not-prose flex flex-row items-center text-fd-card-foreground font-medium has-focus-visible:bg-fd-accent',
|
||||||
|
typeof className === 'function' ? className(s) : className,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Primitive.Header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccordionTrigger({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: ComponentProps<typeof Primitive.Trigger>) {
|
||||||
|
return (
|
||||||
|
<Primitive.Trigger
|
||||||
|
className={(s) =>
|
||||||
|
cn(
|
||||||
|
'group flex flex-1 items-center gap-2 px-3 py-2.5 text-start focus-visible:outline-none',
|
||||||
|
typeof className === 'function' ? className(s) : className,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronRight className="size-4 shrink-0 text-fd-muted-foreground transition-transform duration-200 group-data-[panel-open]:rotate-90" />
|
||||||
|
{children}
|
||||||
|
</Primitive.Trigger>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccordionContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: ComponentProps<typeof Primitive.Panel>) {
|
||||||
|
return (
|
||||||
|
<Primitive.Panel
|
||||||
|
className={(s) =>
|
||||||
|
cn(
|
||||||
|
'h-(--accordion-panel-height) overflow-hidden transition-[height] ease-out data-[ending-style]:h-0 data-[starting-style]:h-0',
|
||||||
|
typeof className === 'function' ? className(s) : className,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Primitive.Panel>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
|
||||||
|
outline: 'border hover:bg-fd-accent hover:text-fd-accent-foreground',
|
||||||
|
ghost: 'hover:bg-fd-accent hover:text-fd-accent-foreground',
|
||||||
|
secondary:
|
||||||
|
'border bg-fd-secondary text-fd-secondary-foreground hover:bg-fd-accent hover:text-fd-accent-foreground',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const buttonVariants = cva(
|
||||||
|
'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fd-ring',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: variants,
|
||||||
|
// fumadocs use `color` instead of `variant`
|
||||||
|
color: variants,
|
||||||
|
size: {
|
||||||
|
sm: 'gap-1 px-2 py-1.5 text-xs',
|
||||||
|
icon: 'p-1.5 [&_svg]:size-5',
|
||||||
|
'icon-sm': 'p-1.5 [&_svg]:size-4.5',
|
||||||
|
'icon-xs': 'p-1 [&_svg]:size-4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export type ButtonProps = VariantProps<typeof buttonVariants>;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { cn } from 'cnfast';
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import type * as React from 'react';
|
||||||
|
|
||||||
|
export function mergeRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> {
|
||||||
|
return (value) => {
|
||||||
|
refs.forEach((ref) => {
|
||||||
|
if (typeof ref === 'function') {
|
||||||
|
ref(value);
|
||||||
|
} else if (ref) {
|
||||||
|
ref.current = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user