New document (#5299)

* add new doc (#5175)

Co-authored-by: dreamer6680 <146868355@qq.com>

* Test docs (#5235)

* fix: change the page of doc

* chore: add new dependencies, update global styles/layout, optimize docs, add Feishu & GitHub icons, update API examples

* fix: docs/index 404 not found

* Update environment variable names, optimize styles, add new API routes, fix component styles, adjust documentation, and update GitHub and Feishu icons

* update readme

* feat: add a linkfastgpt compontent

* feat: update new doc

* fix:remove unuse page and redirect homepage to docs (#5288)

* fix:remove some unuse doc

* fix: redirect homepage to doc

* git ignore

* fix:navbar to index (#5295)

* sidbar

* fix: navtab unlight (#5298)

* doc

---------

Co-authored-by: dreamer6680 <1468683855@qq.com>
Co-authored-by: dreamer6680 <146868355@qq.com>
This commit is contained in:
Archer
2025-07-23 21:35:03 +08:00
committed by GitHub
parent ce9ec1bf57
commit fe7abf22a9
895 changed files with 36297 additions and 56 deletions

View File

@@ -0,0 +1,49 @@
'use client';
// components/CustomSearchDialog.tsx
import { liteClient } from 'algoliasearch/lite';
import { useDocsSearch } from 'fumadocs-core/search/client';
import {
SearchDialog,
SearchDialogOverlay,
SearchDialogContent,
SearchDialogHeader,
SearchDialogIcon,
SearchDialogInput,
SearchDialogClose,
SearchDialogList,
type SharedProps
} from 'fumadocs-ui/components/dialog/search';
import { useI18n } from 'fumadocs-ui/contexts/i18n';
if (!process.env.NEXT_PUBLIC_SEARCH_APPID || !process.env.NEXT_PUBLIC_SEARCH_APPKEY) {
throw new Error('NEXT_PUBLIC_SEARCH_APPID and NEXT_PUBLIC_SEARCH_APPKEY are not set');
}
const client = liteClient(
process.env.NEXT_PUBLIC_SEARCH_APPID,
process.env.NEXT_PUBLIC_SEARCH_APPKEY
);
export default function CustomSearchDialog(props: SharedProps) {
const { locale } = useI18n();
const { search, setSearch, query } = useDocsSearch({
type: 'algolia',
client,
indexName: 'document',
locale
});
return (
<SearchDialog search={search} onSearchChange={setSearch} isLoading={query.isLoading} {...props}>
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader>
<SearchDialogIcon />
<SearchDialogInput />
<SearchDialogClose />
</SearchDialogHeader>
<SearchDialogList items={query.data !== 'empty' ? query.data : null} />
</SearchDialogContent>
</SearchDialog>
);
}

View File

@@ -0,0 +1,35 @@
import type { ReactNode } from 'react';
interface AlertProps {
icon: ReactNode;
context: 'success' | 'warning' | 'error' | 'info';
children: ReactNode;
}
export function Alert({ icon, context = 'info', children }: AlertProps) {
const contextStyles = {
success:
'bg-green-50 border-green-200 text-green-700 dark:bg-white/5 dark:border-teal-300 dark:text-gray-200',
warning:
'bg-yellow-50 border-yellow-200 text-yellow-700 dark:dark:bg-white/5 dark:border-indigo-500 dark:text-gray-200',
error:
'bg-red-50 border-red-200 text-red-700 dark:bg-white/5 dark:border-red-800 dark:text-gray-200',
info: 'bg-blue-50 border-blue-200 text-blue-700 dark:bg-white/5 dark:border-blue-400 dark:text-gray-200'
};
return (
<div
className={`
${contextStyles[context]}
p-4 rounded-lg border
flex gap-3 items-baseline
shadow-sm
transition-all duration-200 ease-in-out
hover:shadow-md
`}
>
<div className="text-2xl flex-shrink-0 mt-0.5">{icon}</div>
<div className="space-y-2 text-sm leading-relaxed flex-grow">{children}</div>
</div>
);
}

View File

@@ -0,0 +1,18 @@
'use client';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
interface RedirectProps {
to: string;
}
export function Redirect({ to }: RedirectProps) {
const router = useRouter();
useEffect(() => {
router.push(to);
}, [to, router]);
return null;
}

View File

@@ -0,0 +1,38 @@
'use client';
import React, { useState } from 'react';
interface TabProps {
title: string;
children: React.ReactNode;
}
interface TabsProps {
children: React.ReactNode;
}
export const Tab: React.FC<TabProps> = ({ children }) => {
return <div>{children}</div>;
};
export const Tabs: React.FC<TabsProps> = ({ children }) => {
const tabs = React.Children.toArray(children) as React.ReactElement<TabProps>[];
const [activeTab, setActiveTab] = useState(0);
return (
<div>
<nav className="nav-tabs">
{tabs.map((tab, index) => (
<button
key={index}
className={`nav-link ${activeTab === index ? 'active' : ''}`}
onClick={() => setActiveTab(index)}
>
{tab.props.title}
</button>
))}
</nav>
<div className="tab-content">{tabs[activeTab]}</div>
</div>
);
};

View File

@@ -0,0 +1,12 @@
export default function YouTube({ id }: { id: string }) {
return (
<div className="border-2 border-black">
<iframe
className="aspect-video w-full"
src={`https://www.youtube.com/embed/${id}`}
title="YouTube Video Player"
allowFullScreen
/>
</div>
);
}

View File

@@ -0,0 +1,28 @@
'use client';
import React from 'react';
const feishuLogoDark: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M17 29C21 29 25 26.9339 28 23.4065C36 14 41.4242 16.8166 44 17.9998C38.5 20.9998 40.5 29.6233 33 35.9998C28.382 39.9259 23.4945 41.014 19 41C12.5231 40.9799 6.86226 37.7637 4 35.4063V16.9998"
stroke="#8b9dc1"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.64808 15.8669C5.02231 14.9567 3.77715 14.7261 2.86694 15.3519C1.95673 15.9777 1.72615 17.2228 2.35192 18.1331L5.64808 15.8669ZM36.0021 35.7309C36.958 35.1774 37.2843 33.9539 36.7309 32.9979C36.1774 32.042 34.9539 31.7157 33.9979 32.2691L36.0021 35.7309ZM2.35192 18.1331C5.2435 22.339 10.7992 28.144 16.8865 32.2239C19.9345 34.2667 23.217 35.946 26.449 36.7324C29.6946 37.522 33.0451 37.4428 36.0021 35.7309L33.9979 32.2691C32.2049 33.3072 29.9929 33.478 27.3947 32.8458C24.783 32.2103 21.9405 30.7958 19.1135 28.9011C13.4508 25.106 8.2565 19.661 5.64808 15.8669L2.35192 18.1331Z"
fill="#8b9dc1"
/>
<path
d="M33.5947 17C32.84 14.7027 30.8551 9.94054 27.5947 7H11.5947C15.2174 10.6757 23.0002 16 27.0002 24"
stroke="#8b9dc1"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
export default feishuLogoDark;

View File

@@ -0,0 +1,28 @@
'use client';
import React from 'react';
const feishuLogoLight: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg width="24" height="24" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M17 29C21 29 25 26.9339 28 23.4065C36 14 41.4242 16.8166 44 17.9998C38.5 20.9998 40.5 29.6233 33 35.9998C28.382 39.9259 23.4945 41.014 19 41C12.5231 40.9799 6.86226 37.7637 4 35.4063V16.9998"
stroke="#8a8a8a"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.64808 15.8669C5.02231 14.9567 3.77715 14.7261 2.86694 15.3519C1.95673 15.9777 1.72615 17.2228 2.35192 18.1331L5.64808 15.8669ZM36.0021 35.7309C36.958 35.1774 37.2843 33.9539 36.7309 32.9979C36.1774 32.042 34.9539 31.7157 33.9979 32.2691L36.0021 35.7309ZM2.35192 18.1331C5.2435 22.339 10.7992 28.144 16.8865 32.2239C19.9345 34.2667 23.217 35.946 26.449 36.7324C29.6946 37.522 33.0451 37.4428 36.0021 35.7309L33.9979 32.2691C32.2049 33.3072 29.9929 33.478 27.3947 32.8458C24.783 32.2103 21.9405 30.7958 19.1135 28.9011C13.4508 25.106 8.2565 19.661 5.64808 15.8669L2.35192 18.1331Z"
fill="#8a8a8a"
/>
<path
d="M33.5947 17C32.84 14.7027 30.8551 9.94054 27.5947 7H11.5947C15.2174 10.6757 23.0002 16 27.0002 24"
stroke="#8a8a8a"
strokeWidth="4"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
export default feishuLogoLight;

View File

@@ -0,0 +1,16 @@
'use client';
import React from 'react';
const githubLogoLight: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg width="98" height="98" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 98">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
fill="#8b9dc1"
/>
</svg>
);
export default githubLogoLight;

View File

@@ -0,0 +1,16 @@
'use client';
import React from 'react';
const githubLogoLight: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg width="98" height="98" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 98">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
fill="#8a8a8a"
/>
</svg>
);
export default githubLogoLight;

View File

@@ -0,0 +1,58 @@
'use client';
import React, { useMemo } from 'react';
type FastGPTLinkProps = {
children: React.ReactNode;
className?: string;
style?: React.CSSProperties;
onClick?: (e: React.MouseEvent<HTMLAnchorElement>) => void;
};
const defaultStyles: React.CSSProperties = {
color: '#3370ff',
textDecoration: 'none',
transition: 'all 0.2s ease-in-out'
};
const hoverStyles: React.CSSProperties = {
color: '#2152d9',
textDecoration: 'underline'
};
const FastGPTLink = ({ children, className, style, onClick, ...props }: FastGPTLinkProps) => {
const href = useMemo(() => {
return process.env.NEXT_PUBLIC_DOMAIN ?? 'https://fastgpt.io';
}, []);
const [isHovered, setIsHovered] = React.useState(false);
const combinedStyles = {
...defaultStyles,
...(isHovered ? hoverStyles : {}),
...style
};
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className={className}
style={combinedStyles}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
onClick={(e) => {
if (onClick) {
e.preventDefault();
onClick(e);
}
}}
{...props}
>
{children}
</a>
);
};
export default React.memo(FastGPTLink);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,81 @@
'use client';
import { usePathname } from 'next/navigation';
import { useEffect, type FC, type ReactNode } from 'react';
import {
SidebarItem,
SidebarFolder,
SidebarFolderTrigger,
SidebarFolderContent
} from 'fumadocs-ui/components/layout/sidebar';
import { type SidebarComponents } from 'fumadocs-ui/components/layout/sidebar';
import { type PageTree } from 'fumadocs-core/server';
const isInFolder = (folder: PageTree.Folder, pathname: string): boolean => {
const check = (item: PageTree.Item | PageTree.Folder): boolean => {
if ('children' in item) {
return item.children
.filter(
(child): child is PageTree.Item | PageTree.Folder => 'url' in child || 'children' in child
)
.some(check);
}
return 'url' in item && item.url === pathname;
};
return check(folder);
};
const CustomItem: FC<{ item: PageTree.Item }> = ({ item }) => {
const pathname = usePathname();
const isActive = pathname === item.url;
useEffect(() => {
if (isActive) {
const anchor = document.querySelector(`a[href='${item.url}']`);
if (anchor) {
setTimeout(() => {
anchor.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 100);
}
}
}, [isActive, item.url]);
return (
<SidebarItem
href={item.url}
className={`rounded-lg hover:cursor-pointer ${isActive && 'bg-blue-50 font-bold text-[#3370FF] dark:bg-[rgba(104,143,232,0.1)] dark:text-blue-400'}
`}
>
{item.icon}
{item.name}
</SidebarItem>
);
};
const CustomFolder: FC<{ item: PageTree.Folder; level: number; children: ReactNode }> = ({
item,
level,
children
}) => {
const pathname = usePathname();
const shouldExpand = isInFolder(item, pathname);
return (
<SidebarFolder defaultOpen={shouldExpand} className="bg-blue hover:cursor-pointer">
<SidebarFolderTrigger className="hover:cursor-pointer">{item.name}</SidebarFolderTrigger>
<SidebarFolderContent className="bg-blue hover:cursor-pointer">
{children}
</SidebarFolderContent>
</SidebarFolder>
);
};
const CustomSeparator: FC<{ item: PageTree.Separator }> = ({ item }) => (
<div className="text-sm font-semibold px-2 py-2 mt-4 mb-2">{item.name}</div>
);
export const CustomSidebarComponents: SidebarComponents = {
Item: CustomItem,
Folder: CustomFolder,
Separator: CustomSeparator
};

View File

@@ -0,0 +1,47 @@
import { type HTMLAttributes } from 'react';
import { HomeLayout, type HomeLayoutProps } from 'fumadocs-ui/layouts/home';
import Link from 'next/link';
interface CustomHomeLayoutProps extends HomeLayoutProps {
// 可以在这里添加自定义的属性
}
export function CustomHomeLayout({
children,
nav,
...props
}: CustomHomeLayoutProps & HTMLAttributes<HTMLElement>) {
return (
<HomeLayout
{...props}
nav={{
...nav,
title: (
<div className="flex flex-col items-center gap-2">
<div className="flex flex-row items-center gap-2">
<img src="/logo.svg" alt="FastGPT" width={49} height={48} />
FastGPT
</div>
<div className="flex flex-row items-center gap-4 text-sm">
<Link href="/docs/introduction" className="hover:text-blue-500">
使
</Link>
<Link href="/docs/use-cases" className="hover:text-blue-500">
使
</Link>
<Link href="/docs/agreement" className="hover:text-blue-500">
</Link>
<Link href="/docs/api" className="hover:text-blue-500">
API手册
</Link>
</div>
</div>
),
transparentMode: 'none'
}}
>
{children}
</HomeLayout>
);
}