通过魔法,在搜索结果中展示API描述
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/dist
|
/dist
|
||||||
/docs/.vitepress/cache/*
|
/docs/.vitepress/cache/*
|
||||||
|
/docs/.vitepress/.temp/*
|
||||||
/docs/.vitepress/dist
|
/docs/.vitepress/dist
|
||||||
.env
|
.env
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineConfig } from 'vitepress';
|
import { defineConfig } from 'vitepress';
|
||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
import generateSidebar from '../../scripts/sidebar.js';
|
import generateSidebar from '../../scripts/sidebar.js';
|
||||||
import { withMermaid } from "vitepress-plugin-mermaid";
|
import { withMermaid } from "vitepress-plugin-mermaid";
|
||||||
|
|
||||||
@@ -8,6 +9,19 @@ export default withMermaid({
|
|||||||
title: "我的世界中国版 ModSDK",
|
title: "我的世界中国版 ModSDK",
|
||||||
description: "ModSDK 开发者文档 镜像,但提供更优质的搜索",
|
description: "ModSDK 开发者文档 镜像,但提供更优质的搜索",
|
||||||
ignoreDeadLinks: true,
|
ignoreDeadLinks: true,
|
||||||
|
// 替换原生搜索组件,自定义展示的搜索结果内容
|
||||||
|
vite: {
|
||||||
|
resolve: {
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
find: /^.*\/VPAlgoliaSearchBox\.vue$/,
|
||||||
|
replacement: fileURLToPath(
|
||||||
|
new URL('./theme/components/AlgoliaSearch.vue', import.meta.url)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
locales: {
|
locales: {
|
||||||
root: {
|
root: {
|
||||||
label: '简体中文',
|
label: '简体中文',
|
||||||
|
|||||||
127
docs/.vitepress/theme/components/AlgoliaSearch.vue
Normal file
127
docs/.vitepress/theme/components/AlgoliaSearch.vue
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import docsearch from '@docsearch/js'
|
||||||
|
import { useRouter, useData } from 'vitepress'
|
||||||
|
import type { DefaultTheme } from 'vitepress/theme'
|
||||||
|
import { h, nextTick, onMounted, watch } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
algolia: DefaultTheme.AlgoliaSearchOptions
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const { site, localeIndex, lang } = useData()
|
||||||
|
|
||||||
|
type DocSearchProps = Parameters<typeof docsearch>[0]
|
||||||
|
|
||||||
|
onMounted(update)
|
||||||
|
watch(localeIndex, update)
|
||||||
|
|
||||||
|
async function update() {
|
||||||
|
await nextTick()
|
||||||
|
const options = {
|
||||||
|
...props.algolia,
|
||||||
|
...props.algolia.locales?.[localeIndex.value]
|
||||||
|
}
|
||||||
|
const rawFacetFilters = options.searchParameters?.facetFilters ?? []
|
||||||
|
const facetFilters = [
|
||||||
|
...(Array.isArray(rawFacetFilters)
|
||||||
|
? rawFacetFilters
|
||||||
|
: [rawFacetFilters]
|
||||||
|
).filter((f) => !f.startsWith('lang:')),
|
||||||
|
`lang:${lang.value}`
|
||||||
|
]
|
||||||
|
initialize({
|
||||||
|
...options,
|
||||||
|
searchParameters: {
|
||||||
|
...options.searchParameters,
|
||||||
|
facetFilters
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从正文中提取"描述"的内容
|
||||||
|
* @param {string} content Markdown内容
|
||||||
|
* @return {string} 提取的描述文本
|
||||||
|
*/
|
||||||
|
function extractAPIDescription(content: string): string | undefined {
|
||||||
|
// 匹配"- 描述"部分,直到下一个部分标题(如"- 参数"、"- 返回值"等)或文档结束
|
||||||
|
const descriptionRegex = /-\s*描述\s*\n([\s\S]*?)(?=\n\s*-\s*(?:参数|返回值|备注|示例)|$)/;
|
||||||
|
const match = content.match(descriptionRegex);
|
||||||
|
|
||||||
|
if (match && match[1]) {
|
||||||
|
// 提取第一行并进行清理:
|
||||||
|
// 1. 移除缩进
|
||||||
|
// 2. 移除 Markdown 符号如 "-", "*", ">", 数字列表前缀 "1." 等
|
||||||
|
const firstLine = match[1].split('\n')[0].trim();
|
||||||
|
return firstLine
|
||||||
|
.replace(/^[\s>*-]*\s*/, '') // 移除开头的 >, *, - 等符号
|
||||||
|
.replace(/^\d+\.\s*/, '') // 移除数字列表前缀,如 "1. "
|
||||||
|
.replace(/^\[.*?\]\s*/, '') // 移除可能的 [xxx] 标记
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize(userOptions: DefaultTheme.AlgoliaSearchOptions) {
|
||||||
|
const options = Object.assign<
|
||||||
|
{},
|
||||||
|
DefaultTheme.AlgoliaSearchOptions,
|
||||||
|
Partial<DocSearchProps>
|
||||||
|
>({}, userOptions, {
|
||||||
|
container: '#docsearch',
|
||||||
|
|
||||||
|
navigator: {
|
||||||
|
navigate({ itemUrl }) {
|
||||||
|
router.go(itemUrl)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 通过魔法,在结果中展示API描述
|
||||||
|
transformItems(items) {
|
||||||
|
return items.map((item) => {
|
||||||
|
const snippetResult = item._snippetResult;
|
||||||
|
if (item.content && snippetResult.hierarchy.lvl2 && !snippetResult.hierarchy.lvl1.value.includes("<br>")) {
|
||||||
|
const description = extractAPIDescription(item.content);
|
||||||
|
if (description) {
|
||||||
|
snippetResult.hierarchy.lvl1.value = description + "<br>" + snippetResult.hierarchy.lvl1.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = Object.assign({}, item, {
|
||||||
|
url: getRelativePath(item.url),
|
||||||
|
_snippetResult: snippetResult,
|
||||||
|
});
|
||||||
|
// console.log('transformItems', result);
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}) as DocSearchProps
|
||||||
|
|
||||||
|
docsearch(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRelativePath(url: string) {
|
||||||
|
const { pathname, hash } = new URL(url, location.origin)
|
||||||
|
return pathname.replace(/\.html$/, site.value.cleanUrls ? '' : '.html') + hash
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="docsearch" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.DocSearch-Hit-Container {
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DocSearch-Hit-content-wrapper {
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DocSearch-Hit-path {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -229,6 +229,31 @@ function getDirectoryRootType(path) {
|
|||||||
return firstDir;
|
return firstDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从正文中提取"描述"的内容
|
||||||
|
* @param {string} content Markdown内容
|
||||||
|
* @return {string} 提取的描述文本
|
||||||
|
*/
|
||||||
|
function extractAPIDescription(content) {
|
||||||
|
// 匹配"- 描述"部分,直到下一个部分标题(如"- 参数"、"- 返回值"等)或文档结束
|
||||||
|
const descriptionRegex = /-\s*描述\s*\n([\s\S]*?)(?=\n\s*-\s*(?:参数|返回值|备注|示例)|$)/;
|
||||||
|
const match = content.match(descriptionRegex);
|
||||||
|
|
||||||
|
if (match && match[1]) {
|
||||||
|
// 提取第一行并进行清理:
|
||||||
|
// 1. 移除缩进
|
||||||
|
// 2. 移除 Markdown 符号如 "-", "*", ">", 数字列表前缀 "1." 等
|
||||||
|
const firstLine = match[1].split('\n')[0].trim();
|
||||||
|
return firstLine
|
||||||
|
.replace(/^[\s>*-]*\s*/, '') // 移除开头的 >, *, - 等符号
|
||||||
|
.replace(/^\d+\.\s*/, '') // 移除数字列表前缀,如 "1. "
|
||||||
|
.replace(/^\[.*?\]\s*/, '') // 移除可能的 [xxx] 标记
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''; // 如果没有找到描述,返回空字符串
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成符合VitePress的Algolia索引记录
|
* 生成符合VitePress的Algolia索引记录
|
||||||
*/
|
*/
|
||||||
@@ -275,6 +300,8 @@ async function generateAlgoliaRecords() {
|
|||||||
const objectID = `${page.path}${anchor}`;
|
const objectID = `${page.path}${anchor}`;
|
||||||
const url = `https://modsdk.easecation.net${page.path}${anchor}`;
|
const url = `https://modsdk.easecation.net${page.path}${anchor}`;
|
||||||
|
|
||||||
|
const description = extractAPIDescription(cleanedContent);
|
||||||
|
|
||||||
// 创建记录
|
// 创建记录
|
||||||
const record = {
|
const record = {
|
||||||
objectID,
|
objectID,
|
||||||
@@ -282,6 +309,7 @@ async function generateAlgoliaRecords() {
|
|||||||
type: level ? `lvl${level}` : 'content', // 标识这是什么级别的标题
|
type: level ? `lvl${level}` : 'content', // 标识这是什么级别的标题
|
||||||
hierarchy,
|
hierarchy,
|
||||||
content: cleanedContent,
|
content: cleanedContent,
|
||||||
|
description,
|
||||||
_tags: ['zh-CN'],
|
_tags: ['zh-CN'],
|
||||||
lang: "zh-CN",
|
lang: "zh-CN",
|
||||||
priority: priority, // 添加优先级字段
|
priority: priority, // 添加优先级字段
|
||||||
@@ -426,7 +454,7 @@ async function settingAlgolia() {
|
|||||||
attributesForFaceting: ['lang', 'type', 'rootType'],
|
attributesForFaceting: ['lang', 'type', 'rootType'],
|
||||||
attributesToHighlight: ['hierarchy.lvl0', 'hierarchy.lvl1', 'hierarchy.lvl2', 'content'],
|
attributesToHighlight: ['hierarchy.lvl0', 'hierarchy.lvl1', 'hierarchy.lvl2', 'content'],
|
||||||
attributesToSnippet: ['content:50'],
|
attributesToSnippet: ['content:50'],
|
||||||
attributesToRetrieve: ['hierarchy', 'content', 'type', 'url', 'lang', 'priority', 'rootType'],
|
attributesToRetrieve: ['hierarchy', 'content', 'description', 'type', 'url', 'lang', 'priority', 'rootType'],
|
||||||
searchableAttributes: [
|
searchableAttributes: [
|
||||||
'hierarchy.lvl0',
|
'hierarchy.lvl0',
|
||||||
'hierarchy.lvl1',
|
'hierarchy.lvl1',
|
||||||
|
|||||||
Reference in New Issue
Block a user