mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 00:34:22 +08:00
feat(docs): enhance documentation structure and add Docker support for standalone Next.js app
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { source } from '@/lib/source';
|
||||
import { createDocsSearchTokenizer } from '@/lib/search-tokenizer';
|
||||
import { createFromSource } from 'fumadocs-core/search/server';
|
||||
|
||||
export const revalidate = false;
|
||||
|
||||
export const { staticGET: GET } = createFromSource(source, {
|
||||
// https://docs.orama.com/docs/orama-js/supported-languages
|
||||
language: 'english',
|
||||
components: {
|
||||
tokenizer: createDocsSearchTokenizer(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,10 +2,19 @@
|
||||
@import 'fumadocs-ui/css/neutral.css';
|
||||
@import 'fumadocs-ui/css/preset.css';
|
||||
|
||||
:root {
|
||||
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
|
||||
--font-display: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif;
|
||||
}
|
||||
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
html > body[data-scroll-locked] {
|
||||
margin-right: 0px !important;
|
||||
--removed-body-scroll-bar-size: 0px !important;
|
||||
|
||||
+1
-18
@@ -1,26 +1,9 @@
|
||||
import { Noto_Sans_SC, Sora } from 'next/font/google';
|
||||
import { Provider } from '@/components/provider';
|
||||
import './global.css';
|
||||
|
||||
const bodyFont = Noto_Sans_SC({
|
||||
subsets: ['latin'],
|
||||
weight: ['400', '500', '600'],
|
||||
variable: '--font-body',
|
||||
});
|
||||
|
||||
const displayFont = Sora({
|
||||
subsets: ['latin'],
|
||||
weight: ['500', '600', '700'],
|
||||
variable: '--font-display',
|
||||
});
|
||||
|
||||
export default function Layout({ children }: LayoutProps<'/'>) {
|
||||
return (
|
||||
<html
|
||||
lang="zh-CN"
|
||||
className={`${bodyFont.className} ${bodyFont.variable} ${displayFont.variable}`}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<html lang="zh-CN" suppressHydrationWarning>
|
||||
<body className="flex flex-col min-h-screen">
|
||||
<Provider>{children}</Provider>
|
||||
</body>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { getPageImage, source } from '@/lib/source';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { generate as DefaultImage } from 'fumadocs-ui/og';
|
||||
import { appName } from '@/lib/shared';
|
||||
|
||||
export const revalidate = false;
|
||||
|
||||
export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...slug]'>) {
|
||||
const { slug } = await params;
|
||||
const page = source.getPage(slug.slice(0, -1));
|
||||
if (!page) notFound();
|
||||
|
||||
return new ImageResponse(
|
||||
<DefaultImage title={page.data.title} description={page.data.description} site={appName} />,
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
return source.getPages().map((page) => ({
|
||||
lang: page.locale,
|
||||
slug: getPageImage(page).segments,
|
||||
}));
|
||||
}
|
||||
@@ -13,12 +13,14 @@ import {
|
||||
import { useDocsSearch } from 'fumadocs-core/search/client';
|
||||
import { create } from '@orama/orama';
|
||||
import { useI18n } from 'fumadocs-ui/contexts/i18n';
|
||||
import { createDocsSearchTokenizer } from '@/lib/search-tokenizer';
|
||||
|
||||
function initOrama() {
|
||||
return create({
|
||||
schema: { _: 'string' },
|
||||
// https://docs.orama.com/docs/orama-js/supported-languages
|
||||
language: 'english',
|
||||
components: {
|
||||
tokenizer: createDocsSearchTokenizer(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getMDXComponents } from '@/components/mdx';
|
||||
import { gitConfig } from '@/lib/shared';
|
||||
import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import { getPageMarkdownUrl, source } from '@/lib/source';
|
||||
import type { Metadata } from 'next';
|
||||
import { createRelativeLink } from 'fumadocs-ui/mdx';
|
||||
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page';
|
||||
@@ -41,8 +41,5 @@ export function getDocPageMetadata(page: DocPageData): Metadata {
|
||||
return {
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
openGraph: {
|
||||
images: getPageImage(page).url,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
type OramaTokenizer = {
|
||||
language: string;
|
||||
normalizationCache: Map<string, string>;
|
||||
tokenize: (raw: string, language?: string, prop?: string, withCache?: boolean) => string[];
|
||||
};
|
||||
|
||||
const wordPattern = /[\p{Script=Han}]+|[a-z0-9][a-z0-9_'-]*/giu;
|
||||
const hanPattern = /^\p{Script=Han}+$/u;
|
||||
const chineseSegmenter = 'Segmenter' in Intl ? new Intl.Segmenter('zh-CN', { granularity: 'word' }) : null;
|
||||
|
||||
function getChineseSegments(value: string) {
|
||||
if (!chineseSegmenter) return [];
|
||||
|
||||
return Array.from(chineseSegmenter.segment(value))
|
||||
.filter((item) => item.isWordLike)
|
||||
.map((item) => item.segment);
|
||||
}
|
||||
|
||||
function addChineseTokens(tokens: string[], value: string) {
|
||||
const chars = Array.from(value);
|
||||
if (chars.length <= 12) tokens.push(value);
|
||||
tokens.push(...getChineseSegments(value));
|
||||
|
||||
for (let size = 1; size <= 3; size += 1) {
|
||||
if (chars.length < size) continue;
|
||||
for (let i = 0; i <= chars.length - size; i += 1) {
|
||||
tokens.push(chars.slice(i, i + size).join(''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createDocsSearchTokenizer(): OramaTokenizer {
|
||||
return {
|
||||
language: 'zh-CN',
|
||||
normalizationCache: new Map(),
|
||||
tokenize(raw) {
|
||||
if (typeof raw !== 'string') return [raw];
|
||||
|
||||
const tokens: string[] = [];
|
||||
const input = raw.normalize('NFKC').toLowerCase();
|
||||
|
||||
for (const match of input.matchAll(wordPattern)) {
|
||||
const value = match[0];
|
||||
if (hanPattern.test(value)) {
|
||||
addChineseTokens(tokens, value);
|
||||
} else {
|
||||
tokens.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(new Set(tokens.filter(Boolean)));
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
export const appName = '无限画布';
|
||||
export const docsRoute = '/docs';
|
||||
export const docsImageRoute = '/og/docs';
|
||||
export const docsContentRoute = '/llms.mdx/docs';
|
||||
|
||||
// fill this with your actual GitHub info, for example:
|
||||
|
||||
+1
-10
@@ -1,6 +1,6 @@
|
||||
import { docs } from 'collections/server';
|
||||
import { loader } from 'fumadocs-core/source';
|
||||
import { docsContentRoute, docsImageRoute, docsRoute } from './shared';
|
||||
import { docsContentRoute, docsRoute } from './shared';
|
||||
|
||||
// See https://fumadocs.dev/docs/headless/source-api for more info
|
||||
export const source = loader({
|
||||
@@ -9,15 +9,6 @@ export const source = loader({
|
||||
plugins: [],
|
||||
});
|
||||
|
||||
export function getPageImage(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'image.png'];
|
||||
|
||||
return {
|
||||
segments,
|
||||
url: `${docsImageRoute}/${segments.join('/')}`,
|
||||
};
|
||||
}
|
||||
|
||||
export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
|
||||
const segments = [...page.slugs, 'content.md'];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user