Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions app/(home)/layout.tsx

This file was deleted.

File renamed without changes.
File renamed without changes.
14 changes: 14 additions & 0 deletions app/[lang]/(home)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { ReactNode } from 'react';
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { baseOptions } from '@/lib/layout.shared';

export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
const { lang } = await params;
return <HomeLayout {...baseOptions(lang)}>{children}</HomeLayout>;
}
217 changes: 161 additions & 56 deletions app/(home)/page.tsx → app/[lang]/(home)/page.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/app/layout.config';
import { baseOptions } from '@/lib/layout.shared';
import { source } from '@/lib/source';
import { GithubInfo } from 'fumadocs-ui/components/github-info';
import { RootToggle } from '@/components/RootToggle';
Expand All @@ -10,12 +10,18 @@ const maps = {
'Tools': <GithubInfo owner="hydro-dev" repo="xcpc-tools" />,
};

export default async function Layout({ children, params }: { children: ReactNode, params: Promise<{ slug?: string[] }> }) {
const { slug } = await params;
export default async function Layout({
children,
params,
}: {
children: ReactNode;
params: Promise<{ lang: string; slug?: string[] }>;
}) {
const { lang, slug } = await params;
return (
<DocsLayout
tree={source.pageTree}
{...baseOptions}
tree={source.getPageTree(lang)}
{...baseOptions(lang)}
links={Object.keys(maps).find((key) => slug?.[0] === key) ? [
{
type: 'custom',
Expand All @@ -30,12 +36,12 @@ export default async function Layout({ children, params }: { children: ReactNode
{
title: 'Hydro',
description: 'The Online Judge System',
url: '/docs/Hydro',
url: `/${lang}/docs/Hydro`,
},
{
title: 'XCPC-Tools',
description: 'Tools for on-site contests',
url: '/docs/Tools',
url: `/${lang}/docs/Tools`,
},
]}
/>
Expand Down
54 changes: 54 additions & 0 deletions app/[lang]/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { source } from '@/lib/source';
import { Popup, PopupContent, PopupTrigger } from 'fumadocs-twoslash/ui';
import { Callout } from 'fumadocs-ui/components/callout';
import {
DocsPage,
DocsBody,
DocsDescription,
DocsTitle,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import defaultMdxComponents from 'fumadocs-ui/mdx';

export default async function Page(props: {
params: Promise<{ lang: string; slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug, params.lang);
if (!page) notFound();

const MDX = page.data.body;

return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX components={{
...defaultMdxComponents,
Popup,
PopupContent,
PopupTrigger,
Callout,
}} />
</DocsBody>
</DocsPage>
);
}

export async function generateStaticParams() {
return source.generateParams();
}

export async function generateMetadata(props: {
params: Promise<{ lang: string; slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug, params.lang);
if (!page) notFound();

return {
title: page.data.title,
description: page.data.description,
};
}
31 changes: 31 additions & 0 deletions app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { RootProvider } from 'fumadocs-ui/provider/next';
import type { ReactNode } from 'react';
import { i18nUI } from '@/lib/layout.shared';
import { i18n } from '@/lib/i18n';

export default async function LangLayout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
const { lang } = await params;

return (
<RootProvider
i18n={i18nUI.provider(lang)}
search={{
options: {
type: 'static',
},
}}
>
{children}
</RootProvider>
);
}

export function generateStaticParams() {
return i18n.languages.map((lang) => ({ lang }));
}
35 changes: 16 additions & 19 deletions app/api/search/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,25 @@ import { stopwords as mandarinStopwords } from "@orama/stopwords/mandarin";

export const revalidate = false;

const tokenizer = createTokenizer({
const mandarinTokenizer = createTokenizer({
language: 'mandarin',
stopWords: mandarinStopwords,
});

const search = {
tokenizer,
components: {
tokenizer,
},
search: {
threshold: 1.5,
tolerance: 2,
boost: {
title: 2,
content: 1,
export const { staticGET: GET } = createFromSource(source, {
localeMap: {
zh: {
tokenizer: mandarinTokenizer,
components: {
tokenizer: mandarinTokenizer,
},
search: {
threshold: 1.5,
tolerance: 2,
},
},
en: {
language: 'english',
},
},
insertOptions: {
batchSize: 100,
async: true,
},
};

export const { staticGET: GET } = createFromSource(source, search);
});
54 changes: 8 additions & 46 deletions app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,16 @@
import { redirect } from 'next/navigation';
import { source } from '@/lib/source';
import { Popup, PopupContent, PopupTrigger } from 'fumadocs-twoslash/ui';
import { Callout } from 'fumadocs-ui/components/callout';
import {
DocsPage,
DocsBody,
DocsDescription,
DocsTitle,
} from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import defaultMdxComponents from 'fumadocs-ui/mdx';

export default async function Page(props: {
export default async function DocsRedirectPage(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();

const MDX = page.data.body;

return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX components={{
...defaultMdxComponents,
Popup,
PopupContent,
PopupTrigger,
Callout,
}} />
</DocsBody>
</DocsPage>
);
const { slug } = await props.params;
const path = slug?.length ? `/zh/docs/${slug.join('/')}` : '/zh/docs';
redirect(path);
}

export async function generateStaticParams() {
return source.generateParams();
}

export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();

return {
title: page.data.title,
description: page.data.description,
};
return source.generateParams()
.filter((p) => p.lang === 'zh')
.map(({ slug }) => ({ slug }));
}
25 changes: 0 additions & 25 deletions app/layout.config.tsx

This file was deleted.

26 changes: 9 additions & 17 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
import './global.css';
import '@/app/global.css';
import 'katex/dist/katex.css';
import { RootProvider } from 'fumadocs-ui/provider/next';
import { Inter } from 'next/font/google';
import type { ReactNode } from 'react';

const inter = Inter({
subsets: ['latin'],
});

export default function Layout({ children }: { children: ReactNode }) {
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en" className={inter.className} suppressHydrationWarning>
<html lang="zh" className={inter.className} suppressHydrationWarning>
<head>
Comment on lines +12 to 13
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The root tag is hardcoded to lang="zh", so English routes (e.g. /en/...) will still announce the document as Chinese, which hurts accessibility/SEO and can affect screen reader pronunciation. Please make the lang attribute reflect the active locale (or restructure layouts so the lang segment can control it).

Copilot uses AI. Check for mistakes.
<title>Hydro Docs</title>
<link rel="icon" href="/favicon.png" />
<meta name="theme-color" content="#ffeded" />
<meta name="description" content="Hydro 文档" />
<meta name="keywords" content="Hydro, 文档, 教程, 指南, HydroOJ, OJ, Online Judge, 在线评测" />
<meta name="og:title" content="Hydro Docs" />
<meta name="og:site_name" content="Hydro Docs" />
<meta name="og:locale" content="zh_CN" />
</head>
<body className="flex flex-col min-h-screen">
<RootProvider
search={{
options: {
type: 'static',
},
}}
>
{children}
</RootProvider>
<script defer src="https://analytics.hydro.ac/script.js" data-website-id="1c3d0070-645b-4b54-b4c8-b7c286daf471"></script>
{children}
<script
defer
src="https://analytics.hydro.ac/script.js"
data-website-id="1c3d0070-645b-4b54-b4c8-b7c286daf471"
/>
</body>
</html>
);
Expand Down
5 changes: 5 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from 'next/navigation';

export default function RootPage() {
redirect('/zh');
}
44 changes: 44 additions & 0 deletions content/docs/Hydro/FAQ/debug.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Debugging Guide
---

## Process Management

Access your server console and use `pm2 ls` to view the status of running processes.
A healthy Hydro instance typically has four processes: `hydrooj`, `hydro-sandbox`, `mongodb`, and `caddy`.

If any processes are missing or failing to start:
1. Run `pm2 stop all` and `pm2 del all`.
2. Rerun the installation script to restore the default process configuration.

### Troubleshooting Process Failures

- **Caddy**: Usually fails due to port conflicts (port 80/443 already in use) or syntax errors in `~/.hydro/Caddyfile`. Run `cd ~/.hydro && caddy run` in the foreground to see detailed error messages.
- **Hydro-Sandbox**: Often fails due to insufficient permissions or an outdated kernel. Check logs with `pm2 logs hydro-sandbox --lines 100`.
- **HydroOJ**: If the main service fails, refer to the section below.

## Debugging HydroOJ

To inspect detailed logs, stop the background process and run Hydro in the foreground:
1. `pm2 stop hydrooj`
2. Run the command `hydrooj` directly.

**Suggested Steps:**
1. **Update**: Ensure you are running the latest version.
2. **Plugins**: Third-party plugins are a common cause of crashes.
- Back up your plugin list: `cp ~/.hydro/addon.json ~/.hydro/addon.json.bak`.
- View active plugins: `hydrooj addon list`.
- Remove non-official plugins (those without the `@hydrooj/` prefix): `hydrooj addon remove <name>`.
3. **Restart**: Check if the system runs correctly without third-party plugins. If it does, re-enable them one by one to identify the culprit.
4. **Support**: If the issue persists, provide the development team with the full console output from startup until the error occurs.

## Frontend Issues

Symptoms include pages failing to load, infinite loading spinners, or error prompts (yellow/red) at the bottom-left of the screen.

1. **Clear Cache**: Press `Ctrl+Shift+Delete` to clear your browser cache and reload.
2. **Browser Version**: Ensure you are using the latest version of Chrome or a Chromium-based browser.
3. **Disable Extensions**: Browser extensions can sometimes interfere with page scripts.
4. **Developer Tools**: Press `F12`, go to the **Console** tab for script errors, and the **Network** tab to identify failed requests.
5. **Report**: When seeking help in the user group, please include screenshots of both the Console and Network tabs.

Loading