mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Core & UI: Support Orama Cloud integration
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'fumadocs-core': minor
|
||||
'fumadocs-ui': minor
|
||||
---
|
||||
|
||||
Support Orama Cloud integration
|
||||
@@ -5,7 +5,7 @@ export const organizationUsers = ['marclave', 'rishi-raj-jain', 'handotdev'];
|
||||
|
||||
export const sponsors = [
|
||||
{
|
||||
url: '',
|
||||
url: 'https://orama.com',
|
||||
label: 'Orama Search',
|
||||
tier: 'golden',
|
||||
logo: (
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { type DocumentRecord } from 'fumadocs-core/search/algolia';
|
||||
import { source } from '@/app/source';
|
||||
import type { StructuredData } from 'fumadocs-core/mdx-plugins';
|
||||
|
||||
export const revalidate = false;
|
||||
|
||||
export type Static = {
|
||||
id: string;
|
||||
structured: StructuredData;
|
||||
tag: string;
|
||||
url: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export async function GET(): Promise<Response> {
|
||||
const pages = source.getPages();
|
||||
const results = await Promise.all<DocumentRecord>(
|
||||
const results = await Promise.all(
|
||||
pages.map(async (page) => {
|
||||
const { structuredData } = await page.data.load();
|
||||
|
||||
return {
|
||||
_id: page.url,
|
||||
id: page.url,
|
||||
structured: structuredData,
|
||||
tag: page.slugs[0],
|
||||
url: page.url,
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
};
|
||||
} satisfies Static;
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import algo from 'algoliasearch/lite';
|
||||
import { OramaClient } from '@oramacloud/client';
|
||||
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
|
||||
import SearchDialog from 'fumadocs-ui/components/dialog/search-algolia';
|
||||
import SearchDialog from 'fumadocs-ui/components/dialog/search-orama';
|
||||
import { useMode } from '@/app/layout.client';
|
||||
|
||||
const appId = process.env.NEXT_PUBLIC_ALGOLIA_APP_ID;
|
||||
const apiKey = process.env.NEXT_PUBLIC_ALGOLIA_API_KEY;
|
||||
const indexName = process.env.NEXT_PUBLIC_ALGOLIA_INDEX;
|
||||
|
||||
if (!appId || !apiKey || !indexName) throw new Error('Algolia credentials');
|
||||
|
||||
const client = algo(appId, apiKey);
|
||||
|
||||
const index = client.initIndex(indexName);
|
||||
const client = new OramaClient({
|
||||
endpoint: 'https://cloud.orama.run/v1/indexes/docs-fk97oe',
|
||||
api_key: 'oPZjdlFbq5BpR54bV5Vj57RYt83Xosk7',
|
||||
});
|
||||
|
||||
export default function CustomSearchDialog(
|
||||
props: SharedProps,
|
||||
): React.ReactElement {
|
||||
return (
|
||||
<SearchDialog
|
||||
index={index}
|
||||
{...props}
|
||||
defaultTag={useMode() ?? 'ui'}
|
||||
allowClear
|
||||
@@ -38,7 +32,8 @@ export default function CustomSearchDialog(
|
||||
value: 'mdx',
|
||||
},
|
||||
]}
|
||||
showAlgolia
|
||||
client={client}
|
||||
showOrama
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import env from '@next/env';
|
||||
import { updateSearchIndexes } from './update-index.mjs';
|
||||
import { updateSearchIndexes } from './update-orama-index.mjs';
|
||||
import { updateOramaAi } from '@/scripts/update-orama-ai.mjs';
|
||||
|
||||
env.loadEnvConfig(process.cwd());
|
||||
|
||||
@@ -26,8 +26,6 @@ export async function updateOramaAi(): Promise<void> {
|
||||
const manager = new CloudManager({ api_key: apiKey });
|
||||
const indexManager = manager.index(index);
|
||||
|
||||
await indexManager.empty();
|
||||
|
||||
const files = await fg([
|
||||
'./content/docs/**/*.mdx',
|
||||
'!*.model.mdx',
|
||||
@@ -65,7 +63,7 @@ export async function updateOramaAi(): Promise<void> {
|
||||
await Promise.all(scan);
|
||||
|
||||
console.log(`added ${records.length} records`);
|
||||
await indexManager.insert(records);
|
||||
await indexManager.snapshot(records);
|
||||
await indexManager.deploy();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { sync } from 'fumadocs-core/search/orama-cloud';
|
||||
import * as fs from 'node:fs/promises';
|
||||
import type { Static } from '@/app/static.json/route';
|
||||
import { CloudManager } from '@oramacloud/client';
|
||||
|
||||
export async function updateSearchIndexes(): Promise<void> {
|
||||
const apiKey = process.env.ORAMA_PRIVATE_API_KEY;
|
||||
|
||||
if (!apiKey) {
|
||||
console.log('no api key for Orama found, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
const content = await fs.readFile('./out/static.json');
|
||||
const records = JSON.parse(content.toString()) as Static[];
|
||||
|
||||
const manager = new CloudManager({ api_key: apiKey });
|
||||
|
||||
await sync(manager, {
|
||||
index: 'twr98yz9itca86121ukrqber',
|
||||
documents: records,
|
||||
});
|
||||
|
||||
console.log(`search updated: ${records.length} records`);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
'use client';
|
||||
|
||||
import algo from 'algoliasearch/lite';
|
||||
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
|
||||
import SearchDialog from 'fumadocs-ui/components/dialog/search-algolia';
|
||||
|
||||
const appId = process.env.NEXT_PUBLIC_ALGOLIA_APP_ID;
|
||||
const apiKey = process.env.NEXT_PUBLIC_ALGOLIA_API_KEY;
|
||||
const indexName = process.env.NEXT_PUBLIC_ALGOLIA_INDEX;
|
||||
|
||||
if (!appId || !apiKey || !indexName) throw new Error('Algolia credentials');
|
||||
|
||||
const client = algo(appId, apiKey);
|
||||
|
||||
const index = client.initIndex(indexName);
|
||||
|
||||
export default function CustomSearchDialog(
|
||||
props: SharedProps,
|
||||
): React.ReactElement {
|
||||
return <SearchDialog index={index} {...props} showAlgolia />;
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
"@types/mdx": "^2.0.13",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"algoliasearch": "4.24.0",
|
||||
"typescript": "^5.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
"import": "./dist/search/algolia.js",
|
||||
"types": "./dist/search/algolia.d.ts"
|
||||
},
|
||||
"./search/orama-cloud": {
|
||||
"import": "./dist/search/orama-cloud.js",
|
||||
"types": "./dist/search/orama-cloud.d.ts"
|
||||
},
|
||||
"./server": {
|
||||
"import": "./dist/server/index.js",
|
||||
"types": "./dist/server/index.d.ts"
|
||||
@@ -94,6 +98,7 @@
|
||||
"devDependencies": {
|
||||
"@algolia/client-search": "4.24.0",
|
||||
"@mdx-js/mdx": "^3.1.0",
|
||||
"@oramacloud/client": "^1.3.18",
|
||||
"@types/estree-jsx": "^1.0.5",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/mdast": "^4.0.3",
|
||||
@@ -114,11 +119,15 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"algoliasearch": "4.24.0",
|
||||
"@oramacloud/client": "1.x.x",
|
||||
"next": "14.x.x || 15.x.x",
|
||||
"react": ">= 18",
|
||||
"react-dom": ">= 18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@oramacloud/client": {
|
||||
"optional": true
|
||||
},
|
||||
"algoliasearch": {
|
||||
"optional": true
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import { type FetchOptions } from '@/search/client/fetch';
|
||||
import { useOnChange } from '@/utils/use-on-change';
|
||||
import { type StaticClient, type StaticOptions } from '@/search/client/static';
|
||||
import { type AlgoliaOptions } from '@/search/client/algolia';
|
||||
import { type OramaCloudOptions } from '@/search/client/orama-cloud';
|
||||
|
||||
interface UseDocsSearch {
|
||||
search: string;
|
||||
@@ -27,7 +28,10 @@ type Client =
|
||||
} & StaticOptions)
|
||||
| ({
|
||||
type: 'algolia';
|
||||
} & AlgoliaOptions);
|
||||
} & AlgoliaOptions)
|
||||
| ({
|
||||
type: 'orama-cloud';
|
||||
} & OramaCloudOptions);
|
||||
|
||||
let staticClient: StaticClient | undefined;
|
||||
|
||||
@@ -55,8 +59,8 @@ export function useDocsSearch(
|
||||
const onStart = useRef<() => void>();
|
||||
|
||||
const cacheKey = useMemo(() => {
|
||||
return key ?? JSON.stringify([client, debouncedValue, locale, tag]);
|
||||
}, [client, debouncedValue, locale, tag, key]);
|
||||
return key ?? JSON.stringify([client.type, debouncedValue, locale, tag]);
|
||||
}, [client.type, debouncedValue, locale, tag, key]);
|
||||
|
||||
useOnChange(cacheKey, () => {
|
||||
const cached = cache.get(cacheKey);
|
||||
@@ -94,6 +98,12 @@ export function useDocsSearch(
|
||||
return searchDocs(index, debouncedValue, tag, rest);
|
||||
}
|
||||
|
||||
if (client.type === 'orama-cloud') {
|
||||
const { searchDocs } = await import('./client/orama-cloud');
|
||||
|
||||
return searchDocs(debouncedValue, tag, client);
|
||||
}
|
||||
|
||||
const { createStaticClient } = await import('./client/static');
|
||||
if (!staticClient) staticClient = createStaticClient(client);
|
||||
|
||||
@@ -118,3 +128,5 @@ export function useDocsSearch(
|
||||
|
||||
return { search, setSearch, query: { isLoading, data: results, error } };
|
||||
}
|
||||
|
||||
export type { OramaCloudOptions, FetchOptions, StaticOptions, AlgoliaOptions };
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { SortedResult } from '@/server';
|
||||
import type { OramaClient, ClientSearchParams } from '@oramacloud/client';
|
||||
import { removeUndefined } from '@/utils/remove-undefined';
|
||||
import type { OramaIndex } from '@/search/orama-cloud';
|
||||
|
||||
export interface OramaCloudOptions {
|
||||
client: OramaClient;
|
||||
params?: ClientSearchParams;
|
||||
}
|
||||
|
||||
export async function searchDocs(
|
||||
query: string,
|
||||
tag: string | undefined,
|
||||
options: OramaCloudOptions,
|
||||
): Promise<SortedResult[]> {
|
||||
const { client, params: extraParams = {} } = options;
|
||||
|
||||
const params: ClientSearchParams = {
|
||||
...extraParams,
|
||||
term: query,
|
||||
where: removeUndefined({
|
||||
tag,
|
||||
...extraParams.where,
|
||||
}),
|
||||
groupBy: {
|
||||
properties: ['page_id'],
|
||||
maxResult: 7,
|
||||
...extraParams.groupBy,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await client.search(params);
|
||||
if (!result) return [];
|
||||
|
||||
const list: SortedResult[] = [];
|
||||
for (const item of result.groups ?? []) {
|
||||
let addedHead = false;
|
||||
|
||||
for (const hit of item.result) {
|
||||
const doc = hit.document as OramaIndex;
|
||||
|
||||
if (!addedHead) {
|
||||
list.push({
|
||||
id: doc.page_id,
|
||||
type: 'page',
|
||||
content: doc.title,
|
||||
url: doc.url,
|
||||
});
|
||||
addedHead = true;
|
||||
}
|
||||
|
||||
list.push({
|
||||
id: doc.id,
|
||||
content: doc.content,
|
||||
type: doc.content === doc.section ? 'heading' : 'text',
|
||||
url: doc.url,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import type { CloudManager } from '@oramacloud/client';
|
||||
import type { StructuredData } from '@/mdx-plugins';
|
||||
|
||||
export interface SyncOptions {
|
||||
/**
|
||||
* Index name to sync
|
||||
*/
|
||||
index: string;
|
||||
|
||||
documents: OramaDocument[];
|
||||
|
||||
/**
|
||||
* Deploy changes
|
||||
*
|
||||
* @defaultValue true
|
||||
*/
|
||||
autoDeploy?: boolean;
|
||||
}
|
||||
|
||||
export type I18nSyncOptions = Omit<SyncOptions, 'index' | 'documents'> & {
|
||||
/**
|
||||
* Indexes to sync.
|
||||
*
|
||||
* Pairs of `locale`-`index`.
|
||||
**/
|
||||
indexes: Record<string, string>;
|
||||
|
||||
documents: {
|
||||
locale: string;
|
||||
items: OramaDocument[];
|
||||
}[];
|
||||
};
|
||||
|
||||
export interface OramaDocument {
|
||||
/**
|
||||
* The ID of document, must be unique
|
||||
*/
|
||||
id: string;
|
||||
|
||||
title: string;
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* URL to the page
|
||||
*/
|
||||
url: string;
|
||||
structured: StructuredData;
|
||||
|
||||
/**
|
||||
* Tag to filter results
|
||||
*/
|
||||
tag?: string;
|
||||
|
||||
/**
|
||||
* Data to be added to each section index
|
||||
*/
|
||||
extra_data?: object;
|
||||
}
|
||||
|
||||
export interface OramaIndex {
|
||||
id: string;
|
||||
|
||||
title: string;
|
||||
url: string;
|
||||
|
||||
tag?: string;
|
||||
|
||||
/**
|
||||
* The id of page, used for `group by`
|
||||
*/
|
||||
page_id: string;
|
||||
|
||||
/**
|
||||
* Heading content
|
||||
*/
|
||||
section?: string;
|
||||
|
||||
/**
|
||||
* Heading (anchor) id
|
||||
*/
|
||||
section_id?: string;
|
||||
|
||||
content: string;
|
||||
}
|
||||
|
||||
export async function sync(
|
||||
cloudManager: CloudManager,
|
||||
options: SyncOptions,
|
||||
): Promise<void> {
|
||||
const { autoDeploy = true } = options;
|
||||
const index = cloudManager.index(options.index);
|
||||
|
||||
await index.snapshot(options.documents.flatMap(toIndex));
|
||||
if (autoDeploy) await index.deploy();
|
||||
}
|
||||
|
||||
const a = {
|
||||
id: 'string',
|
||||
title: 'string',
|
||||
url: 'string',
|
||||
tag: 'string',
|
||||
page_id: 'string',
|
||||
section: 'string',
|
||||
section_id: 'string',
|
||||
content: 'string',
|
||||
};
|
||||
|
||||
export async function syncI18n(
|
||||
cloudManager: CloudManager,
|
||||
options: I18nSyncOptions,
|
||||
): Promise<void> {
|
||||
const { autoDeploy = true } = options;
|
||||
|
||||
const tasks = options.documents.map(async (document) => {
|
||||
const index = cloudManager.index(options.indexes[document.locale]);
|
||||
|
||||
await index.snapshot(document.items.flatMap(toIndex));
|
||||
if (autoDeploy) await index.deploy();
|
||||
});
|
||||
|
||||
await Promise.all(tasks);
|
||||
}
|
||||
|
||||
function toIndex(page: OramaDocument): OramaIndex[] {
|
||||
let id = 0;
|
||||
const indexes: OramaIndex[] = [];
|
||||
const scannedHeadings = new Set<string>();
|
||||
|
||||
function createIndex(
|
||||
section: string | undefined,
|
||||
sectionId: string | undefined,
|
||||
content: string,
|
||||
): OramaIndex {
|
||||
return {
|
||||
id: `${page.id}-${(id++).toString()}`,
|
||||
title: page.title,
|
||||
url: page.url,
|
||||
page_id: page.id,
|
||||
tag: page.tag,
|
||||
section,
|
||||
section_id: sectionId,
|
||||
content,
|
||||
...page.extra_data,
|
||||
};
|
||||
}
|
||||
|
||||
if (page.description)
|
||||
indexes.push(createIndex(undefined, undefined, page.description));
|
||||
|
||||
page.structured.contents.forEach((p) => {
|
||||
const heading = p.heading
|
||||
? page.structured.headings.find((h) => p.heading === h.id)
|
||||
: null;
|
||||
|
||||
const index = createIndex(heading?.content, heading?.id, p.content);
|
||||
|
||||
if (heading && !scannedHeadings.has(heading.id)) {
|
||||
scannedHeadings.add(heading.id);
|
||||
|
||||
indexes.push(createIndex(heading.content, heading.id, heading.content));
|
||||
}
|
||||
|
||||
indexes.push(index);
|
||||
});
|
||||
|
||||
return indexes;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export default defineConfig({
|
||||
'src/search/client.ts',
|
||||
'src/search/server.ts',
|
||||
'src/search/algolia.ts',
|
||||
'src/search/orama-cloud.ts',
|
||||
'src/utils/use-on-change.ts',
|
||||
'src/utils/use-shiki.tsx',
|
||||
'src/i18n/index.ts',
|
||||
|
||||
@@ -81,12 +81,10 @@
|
||||
"tailwind-merge": "^2.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@algolia/client-search": "4.24.0",
|
||||
"@fumadocs/cli": "workspace:*",
|
||||
"@next/eslint-plugin-next": "^15.0.1",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"algoliasearch": "4.24.0",
|
||||
"eslint-config-custom": "workspace:*",
|
||||
"lucide-react": "^0.453.0",
|
||||
"next": "15.0.2",
|
||||
@@ -99,13 +97,14 @@
|
||||
"peerDependencies": {
|
||||
"next": "14.x.x || 15.x.x",
|
||||
"react": ">= 18",
|
||||
"react-dom": ">= 18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@algolia/client-search": "^4.0.0",
|
||||
"algoliasearch": "^4.24.0",
|
||||
"react-dom": ">= 18",
|
||||
"tailwindcss": "^3.4.14"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"tailwindcss": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import type { SearchIndex } from 'algoliasearch/lite';
|
||||
import { useDocsSearch } from 'fumadocs-core/search/client';
|
||||
import {
|
||||
useDocsSearch,
|
||||
type AlgoliaOptions,
|
||||
} from 'fumadocs-core/search/client';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
import { useOnChange } from 'fumadocs-core/utils/use-on-change';
|
||||
import type { SearchOptions } from '@algolia/client-search';
|
||||
import {
|
||||
SearchDialog,
|
||||
type SharedProps,
|
||||
@@ -13,8 +14,8 @@ import {
|
||||
} from './search';
|
||||
|
||||
export interface AlgoliaSearchDialogProps extends SharedProps {
|
||||
index: SearchIndex;
|
||||
searchOptions?: SearchOptions;
|
||||
index: AlgoliaOptions['index'];
|
||||
searchOptions?: Omit<AlgoliaOptions, 'index'>;
|
||||
footer?: ReactNode;
|
||||
|
||||
defaultTag?: string;
|
||||
@@ -43,7 +44,7 @@ export default function AlgoliaSearchDialog({
|
||||
showAlgolia = false,
|
||||
allowClear = false,
|
||||
...props
|
||||
}: AlgoliaSearchDialogProps): React.ReactElement {
|
||||
}: AlgoliaSearchDialogProps): ReactNode {
|
||||
const [tag, setTag] = useState(defaultTag);
|
||||
const { search, setSearch, query } = useDocsSearch(
|
||||
{
|
||||
@@ -87,7 +88,7 @@ export default function AlgoliaSearchDialog({
|
||||
);
|
||||
}
|
||||
|
||||
function AlgoliaTitle(): React.ReactNode {
|
||||
function AlgoliaTitle(): ReactNode {
|
||||
return (
|
||||
<a
|
||||
href="https://algolia.com"
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
useDocsSearch,
|
||||
type OramaCloudOptions,
|
||||
} from 'fumadocs-core/search/client';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
import { useOnChange } from 'fumadocs-core/utils/use-on-change';
|
||||
import {
|
||||
SearchDialog,
|
||||
type SharedProps,
|
||||
type TagItem,
|
||||
TagsList,
|
||||
} from './search';
|
||||
|
||||
export interface OramaSearchDialogProps extends SharedProps {
|
||||
client: OramaCloudOptions['client'];
|
||||
searchOptions?: OramaCloudOptions['params'];
|
||||
footer?: ReactNode;
|
||||
|
||||
defaultTag?: string;
|
||||
tags?: TagItem[];
|
||||
|
||||
/**
|
||||
* Add the "Powered by Orama" label
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
showOrama?: boolean;
|
||||
|
||||
/**
|
||||
* Allow to clear tag filters
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
allowClear?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orama Cloud integration
|
||||
*/
|
||||
export default function OramaSearchDialog({
|
||||
client,
|
||||
searchOptions,
|
||||
tags,
|
||||
defaultTag,
|
||||
showOrama = false,
|
||||
allowClear = false,
|
||||
...props
|
||||
}: OramaSearchDialogProps): ReactNode {
|
||||
const [tag, setTag] = useState(defaultTag);
|
||||
const { search, setSearch, query } = useDocsSearch(
|
||||
{
|
||||
type: 'orama-cloud',
|
||||
client,
|
||||
params: searchOptions,
|
||||
},
|
||||
undefined,
|
||||
tag,
|
||||
);
|
||||
|
||||
useOnChange(defaultTag, (v) => {
|
||||
setTag(v);
|
||||
});
|
||||
|
||||
return (
|
||||
<SearchDialog
|
||||
search={search}
|
||||
onSearchChange={setSearch}
|
||||
results={query.data ?? []}
|
||||
isLoading={query.isLoading}
|
||||
{...props}
|
||||
footer={
|
||||
tags ? (
|
||||
<>
|
||||
<TagsList
|
||||
tag={tag}
|
||||
onTagChange={setTag}
|
||||
items={tags}
|
||||
allowClear={allowClear}
|
||||
>
|
||||
{showOrama ? <Label /> : null}
|
||||
</TagsList>
|
||||
{props.footer}
|
||||
</>
|
||||
) : (
|
||||
props.footer
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function Label(): ReactNode {
|
||||
return (
|
||||
<a
|
||||
href="https://orama.com"
|
||||
rel="noreferrer noopener"
|
||||
className="ms-auto text-xs text-fd-muted-foreground"
|
||||
>
|
||||
Search powered by Orama
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ const exportedComponents = [
|
||||
'dialog/search',
|
||||
'dialog/search-default',
|
||||
'dialog/search-algolia',
|
||||
'dialog/search-orama',
|
||||
'layout/root-toggle',
|
||||
'layout/language-toggle',
|
||||
];
|
||||
|
||||
Generated
+9
-10
@@ -342,6 +342,9 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
algoliasearch:
|
||||
specifier: 4.24.0
|
||||
version: 4.24.0
|
||||
typescript:
|
||||
specifier: ^5.6.3
|
||||
version: 5.6.3
|
||||
@@ -559,6 +562,9 @@ importers:
|
||||
'@mdx-js/mdx':
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0([email protected])
|
||||
'@oramacloud/client':
|
||||
specifier: ^1.3.18
|
||||
version: 1.3.18([email protected])([email protected])
|
||||
'@types/estree-jsx':
|
||||
specifier: ^1.0.5
|
||||
version: 1.0.5
|
||||
@@ -1134,16 +1140,6 @@ importers:
|
||||
tailwind-merge:
|
||||
specifier: ^2.5.4
|
||||
version: 2.5.4
|
||||
optionalDependencies:
|
||||
'@algolia/client-search':
|
||||
specifier: ^4.0.0
|
||||
version: 4.24.0
|
||||
algoliasearch:
|
||||
specifier: ^4.24.0
|
||||
version: 4.24.0
|
||||
tailwindcss:
|
||||
specifier: ^3.4.14
|
||||
version: 3.4.14
|
||||
devDependencies:
|
||||
'@fumadocs/cli':
|
||||
specifier: workspace:*
|
||||
@@ -1175,6 +1171,9 @@ importers:
|
||||
postcss-lightningcss:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1([email protected])
|
||||
tailwindcss:
|
||||
specifier: ^3.4.13
|
||||
version: 3.4.14
|
||||
tsconfig:
|
||||
specifier: workspace:*
|
||||
version: link:../tsconfig
|
||||
|
||||
Reference in New Issue
Block a user