mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 10:55:57 +00:00
Astro: make search working
This commit is contained in:
@@ -2,11 +2,30 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
import react from '@astrojs/react';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import mdx from 'fumadocs-mdx/astro';
|
||||
import * as SourceConfig from './source.config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import {
|
||||
rehypeCode,
|
||||
remarkCodeTab,
|
||||
remarkHeading,
|
||||
remarkNpm,
|
||||
remarkStructure,
|
||||
} from 'fumadocs-core/mdx-plugins';
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [react(), mdx(SourceConfig)],
|
||||
integrations: [
|
||||
react(),
|
||||
mdx({
|
||||
extendMarkdownConfig: false,
|
||||
syntaxHighlight: false,
|
||||
remarkPlugins: [
|
||||
remarkHeading,
|
||||
remarkCodeTab,
|
||||
remarkNpm,
|
||||
[remarkStructure, { exportAs: 'structuredData' }],
|
||||
],
|
||||
rehypePlugins: [rehypeCode],
|
||||
}),
|
||||
],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ Welcome to the docs! You can start writing documents in `/content/docs`.
|
||||
console.log('Hello World');
|
||||
```
|
||||
|
||||
## What is Next?
|
||||
## What is `Next`?
|
||||
|
||||
<Cards>
|
||||
<Card title="Learn more about Astro" href="https://astro.build">
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"pages": ["..."]
|
||||
}
|
||||
@@ -13,9 +13,9 @@
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "^4.3.9",
|
||||
"@astrojs/react": "^4.4.1",
|
||||
"@orama/orama": "^3.1.16",
|
||||
"astro": "^5.6.1",
|
||||
"fumadocs-core": "workspace:*",
|
||||
"fumadocs-mdx": "workspace:*",
|
||||
"fumadocs-ui": "workspace:*",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { defineDocs } from 'fumadocs-mdx/config';
|
||||
|
||||
export const docs = defineDocs({
|
||||
dir: 'content/docs',
|
||||
});
|
||||
@@ -5,6 +5,7 @@ import type { ReactNode } from 'react';
|
||||
import { FrameworkProvider } from 'fumadocs-core/framework';
|
||||
import { navigate } from 'astro:transitions/client';
|
||||
import { RootProvider } from 'fumadocs-ui/provider/base';
|
||||
import SearchDialog from './search';
|
||||
|
||||
export function Docs({
|
||||
tree,
|
||||
@@ -32,9 +33,12 @@ export function Docs({
|
||||
},
|
||||
})}
|
||||
>
|
||||
<RootProvider theme={{ enabled: false }}>
|
||||
<RootProvider theme={{ enabled: false }} search={{ SearchDialog }}>
|
||||
<DocsLayout
|
||||
tree={tree}
|
||||
themeSwitch={{
|
||||
enabled: false,
|
||||
}}
|
||||
nav={{
|
||||
title: 'Fumadocs on Astro',
|
||||
}}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ClientRouter } from 'astro:transitions';
|
||||
<ClientRouter />
|
||||
</head>
|
||||
<body
|
||||
class="dark"
|
||||
class="dark flex flex-col min-h-screen"
|
||||
transition:persist
|
||||
transition:animate="none"
|
||||
transition:name="page-layout"
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
'use client';
|
||||
import {
|
||||
SearchDialog,
|
||||
SearchDialogClose,
|
||||
SearchDialogContent,
|
||||
SearchDialogHeader,
|
||||
SearchDialogIcon,
|
||||
SearchDialogInput,
|
||||
SearchDialogList,
|
||||
SearchDialogOverlay,
|
||||
type SharedProps,
|
||||
} from 'fumadocs-ui/components/dialog/search';
|
||||
import { useDocsSearch } from 'fumadocs-core/search/client';
|
||||
import { create } from '@orama/orama';
|
||||
import { useI18n } from 'fumadocs-ui/contexts/i18n';
|
||||
|
||||
function initOrama() {
|
||||
return create({
|
||||
schema: { _: 'string' },
|
||||
// https://docs.orama.com/docs/orama-js/supported-languages
|
||||
language: 'english',
|
||||
});
|
||||
}
|
||||
|
||||
export default function DefaultSearchDialog(props: SharedProps) {
|
||||
const { locale } = useI18n(); // (optional) for i18n
|
||||
const { search, setSearch, query } = useDocsSearch({
|
||||
type: 'static',
|
||||
initOrama,
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -6,9 +6,21 @@ const docs = defineCollection({
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string().optional(),
|
||||
icon: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
const meta = defineCollection({
|
||||
loader: glob({ pattern: '**/*.{json,yaml}', base: './content/docs' }),
|
||||
schema: z.object({
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
pages: z.array(z.string()).optional(),
|
||||
icon: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
docs,
|
||||
meta,
|
||||
};
|
||||
|
||||
@@ -1,34 +1,54 @@
|
||||
import type { MetaData, Source } from 'fumadocs-core/source';
|
||||
import type { Source } from 'fumadocs-core/source';
|
||||
import { loader } from 'fumadocs-core/source';
|
||||
import { type CollectionEntry, getCollection } from 'astro:content';
|
||||
import * as path from 'node:path';
|
||||
import type { StructuredData } from 'fumadocs-core/mdx-plugins';
|
||||
|
||||
export const source = loader({
|
||||
source: await createMySource(),
|
||||
baseUrl: '/',
|
||||
});
|
||||
|
||||
async function createMySource(): Promise<
|
||||
Source<{
|
||||
metaData: MetaData;
|
||||
const docs = import.meta.glob('/content/docs/**/*.{md,mdx}');
|
||||
|
||||
export async function getFullExport(entry: CollectionEntry<'docs'>) {
|
||||
return (await docs['/' + entry.filePath!]()) as {
|
||||
structuredData: StructuredData;
|
||||
};
|
||||
}
|
||||
|
||||
async function createMySource() {
|
||||
const out: Source<{
|
||||
metaData: CollectionEntry<'meta'>['data'];
|
||||
pageData: CollectionEntry<'docs'>['data'] & {
|
||||
_raw: CollectionEntry<'docs'>;
|
||||
};
|
||||
}>
|
||||
> {
|
||||
const pages = await getCollection('docs');
|
||||
|
||||
return {
|
||||
files: pages.map((page) => {
|
||||
const virtualPath = path.relative('content/docs', page.filePath!);
|
||||
return {
|
||||
type: 'page',
|
||||
path: virtualPath,
|
||||
data: {
|
||||
...page.data,
|
||||
_raw: page,
|
||||
},
|
||||
};
|
||||
}),
|
||||
}> = {
|
||||
files: [],
|
||||
};
|
||||
|
||||
for (const page of await getCollection('docs')) {
|
||||
const virtualPath = path.relative('content/docs', page.filePath!);
|
||||
|
||||
out.files.push({
|
||||
type: 'page',
|
||||
path: virtualPath,
|
||||
data: {
|
||||
...page.data,
|
||||
_raw: page,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (const meta of await getCollection('meta')) {
|
||||
const virtualPath = path.relative('content/docs', meta.filePath!);
|
||||
|
||||
out.files.push({
|
||||
type: 'meta',
|
||||
path: virtualPath,
|
||||
data: meta.data,
|
||||
});
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
import '../styles/global.css';
|
||||
import { source } from '../lib/source';
|
||||
import Layout from '../components/layout.astro';
|
||||
import type { TOCItemType } from 'fumadocs-core/toc';
|
||||
|
||||
interface Props {
|
||||
page: CollectionEntry<'docs'>;
|
||||
@@ -18,7 +19,13 @@ export async function getStaticPaths() {
|
||||
}
|
||||
|
||||
const { page } = Astro.props;
|
||||
const { Content } = await render(page);
|
||||
const { Content, headings } = await render(page);
|
||||
|
||||
const toc: TOCItemType[] = headings.map((heading) => ({
|
||||
depth: heading.depth,
|
||||
title: heading.text,
|
||||
url: '#' + heading.slug,
|
||||
}));
|
||||
---
|
||||
|
||||
<Layout>
|
||||
@@ -29,6 +36,7 @@ const { Content } = await render(page);
|
||||
tree={source.pageTree}
|
||||
pathname={Astro.url.pathname}
|
||||
params={Astro.params as Record<string, string | string[]>}
|
||||
page={{ toc }}
|
||||
client:load
|
||||
>
|
||||
<h1 class="font-semibold text-3xl">{page.data.title}</h1>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { createFromSource } from 'fumadocs-core/search/server';
|
||||
import { getFullExport, source } from '../../lib/source';
|
||||
import { getBreadcrumbItems } from 'fumadocs-core/breadcrumb';
|
||||
|
||||
const server = createFromSource(source, {
|
||||
async buildIndex(page) {
|
||||
const { structuredData } = await getFullExport(page.data._raw);
|
||||
console.log(structuredData);
|
||||
|
||||
return {
|
||||
id: page.data._raw.id,
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
structuredData,
|
||||
url: page.url,
|
||||
breadcrumbs: getBreadcrumbItems(page.url, source.pageTree).map((item) =>
|
||||
String(item.name),
|
||||
),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const GET: APIRoute = () => {
|
||||
return server.staticGET();
|
||||
};
|
||||
@@ -1,7 +1,3 @@
|
||||
@import 'tailwindcss';
|
||||
@import 'fumadocs-ui/css/neutral.css';
|
||||
@import 'fumadocs-ui/css/preset.css';
|
||||
|
||||
body {
|
||||
@apply flex flex-col min-h-screen;
|
||||
}
|
||||
|
||||
Generated
+9
-6
@@ -306,15 +306,15 @@ importers:
|
||||
'@astrojs/react':
|
||||
specifier: ^4.4.1
|
||||
version: 4.4.1(@types/[email protected])(@types/[email protected](@types/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected])
|
||||
'@orama/orama':
|
||||
specifier: ^3.1.16
|
||||
version: 3.1.16
|
||||
astro:
|
||||
specifier: ^5.6.1
|
||||
version: 5.15.2(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
|
||||
fumadocs-core:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core
|
||||
fumadocs-mdx:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/mdx
|
||||
fumadocs-ui:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/ui
|
||||
@@ -1516,6 +1516,9 @@ importers:
|
||||
'@shikijs/transformers':
|
||||
specifier: ^3.14.0
|
||||
version: 3.14.0
|
||||
estree-util-value-to-estree:
|
||||
specifier: ^3.5.0
|
||||
version: 3.5.0
|
||||
github-slugger:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@@ -4250,8 +4253,8 @@ packages:
|
||||
'@radix-ui/[email protected]':
|
||||
resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==}
|
||||
peerDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.2
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
@@ -9282,7 +9285,7 @@ packages:
|
||||
resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react': '*'
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
|
||||
Reference in New Issue
Block a user